Cancellation Token in C# - Part 7
در آخرین قسمت از این سری ویدیوها در مورد کنسلیشن توکن صحبت کردیم و چرایی وجودش و اینکه این توکن رو چه کسی درست میکنه کی درست میکنه چطور مدیریت میکنه.
05:10 Scenario without cancellation token
13:40 What is the cancellation token source
14:53 Demo
18:00 Cancellation Exception
19:10 Review Code of Task Class
21:09 Demo on Asp dotnet core
مدت ویدیو : 27 دقیقه
قسمت 4- تو این ویدیو کامل مباحثی مثل Lock وMonitor وMutex وSemaphore رو بررسی کردیم و 2 نمونه از همزمانی و ددلاک رو هم توی کد دیدیم.
1:00 Why do we call the worker thread
2:47 Deadlock
09:29 Monitor and lock the object
12:36 lock(){} is Monitor
13:15 Mutex/ Semaphore/ SemaphoreSlim
لطفا در چنل یوتیوب ما عضو شوید، عضو شدن شما میتونه انرژی خوبی برای ما باشه برای تولید محتوهای بعدی، ممنونم .
مدت ویدیو : 22 دقیقه
در ادامه قسمت اول اینسری در مورد فرق بین پروسس و ترد صحبت کردیم و انواع بلاک شدن و کانتکست سوییچ بین تردهای درون یه پروسس صحبت کردیم.
01:10 Main Thread
02:50 Multi-Thread Process
04:25 Thread for GC & Finalization
05:22 Demo
08:00 Context switch on threads
10:50 Thread Blocking
15:11 Blocking Type (I/O and Compute Bound)
مدت زمان ویدیو : 19 دقیقه
مدیریت مباحث همزمانی مرتبط با یک Rich Domain Model با استفاده از EFCore و الگوی Aggregate
In summary, the most important issues here are:
- The Aggregate’s main task is to protect invariants (business rules, the boundary of immediate consistency)
- In a multi-threaded environment, when multiple threads are running simultaneously on the same Aggregate, a business rule may be broken
- A way to solve concurrency conflicts is to use Pessimistic or Optimistic concurrency techniques
- Pessimistic Concurrency involves the use of a database transaction and a locking mechanism. In this way, requests are processed one after the other, so basically concurrency is lost and it can lead to deadlocks.
- Optimistic Concurrency technique is based on versioning database records and checking whether the previously loaded version has not been changed by another thread.
- Entity Framework Core supports Optimistic Concurrency. Pessimistic Concurrency is not supported
- The Aggregate must always be treated and versioned as a single unit
- Domain events are an indicator, that state was changed so Aggregate version should be changed as well
public class AggregateRootBase : Entity, IAggregateRoot { private int _versionId; public void IncreaseVersion() { _versionId++; } }
internal sealed class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order> { public void Configure(EntityTypeBuilder<Order> builder) { builder.Property("_versionId").HasColumnName("VersionId").IsConcurrencyToken(); //... } }
var order = await _ordersContext.Orders.FindAsync(orderId); order.AddOrderLine(request.ProductCode); var domainEvents = DomainEventsHelper.GetAllDomainEvents(order); if (domainEvents.Any()) { order.IncreaseVersion(); } await _ordersContext.SaveChangesAsync();
public class HandleConcurrencyExceptionAttribute : FilterAttribute, IExceptionFilter { private PropertyMatchingMode _propertyMatchingMode; /// <summary> /// This defines when the concurrencyexception happens, /// </summary> public enum PropertyMatchingMode { /// <summary> /// Uses only the field names in the model to check against the entity. This option is best when you are using /// View Models with limited fields as opposed to an entity that has many fields. The ViewModel (or model) field names will /// be used to check current posted values vs. db values on the entity itself. /// </summary> UseViewModelNamesToCheckEntity = 0, /// <summary> /// Use any non-matching value fields on the entity (except timestamp fields) to add errors to the ModelState. /// </summary> UseEntityFieldsOnly = 1, /// <summary> /// Tells the filter to not attempt to add field differences to the model state. /// This means the end user will not see the specifics of which fields caused issues /// </summary> DontDisplayFieldClashes = 2 } public HandleConcurrencyExceptionAttribute() { _propertyMatchingMode = PropertyMatchingMode.UseViewModelNamesToCheckEntity; } public HandleConcurrencyExceptionAttribute(PropertyMatchingMode propertyMatchingMode) { _propertyMatchingMode = propertyMatchingMode; } /// <summary> /// The main method, called by the mvc runtime when an exception has occured. /// This must be added as a global filter, or as an attribute on a class or action method. /// </summary> /// <param name="filterContext"></param> public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled && filterContext.Exception is DbUpdateConcurrencyException) { //Get original and current entity values DbUpdateConcurrencyException ex = (DbUpdateConcurrencyException)filterContext.Exception; var entry = ex.Entries.Single(); //problems with ef4.1/4.2 here because of context/model in different projects. //var databaseValues = entry.CurrentValues.Clone().ToObject(); //var clientValues = entry.Entity; //So - if using EF 4.1/4.2 you may use this workaround var clientValues = entry.CurrentValues.Clone().ToObject(); entry.Reload(); var databaseValues = entry.CurrentValues.ToObject(); List<string> propertyNames; filterContext.Controller.ViewData.ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The " + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again to cause your changes to be the current saved values."); PropertyInfo[] entityFromDbProperties = databaseValues.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); if (_propertyMatchingMode == PropertyMatchingMode.UseViewModelNamesToCheckEntity) { //We dont have access to the model here on an exception. Get the field names from modelstate: propertyNames = filterContext.Controller.ViewData.ModelState.Keys.ToList(); } else if (_propertyMatchingMode == PropertyMatchingMode.UseEntityFieldsOnly) { propertyNames = databaseValues.GetType().GetProperties(BindingFlags.Public).Select(o => o.Name).ToList(); } else { filterContext.ExceptionHandled = true; UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; return; } UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); //Get all public properties of the entity that have names matching those in our modelstate. foreach (var propertyInfo in entityFromDbProperties) { //If this value is not in the ModelState values, don't compare it as we don't want //to attempt to emit model errors for fields that don't exist. //Compare db value to the current value from the entity we posted. if (propertyNames.Contains(propertyInfo.Name)) { if (propertyInfo.GetValue(databaseValues, null) != propertyInfo.GetValue(clientValues, null)) { var currentValue = propertyInfo.GetValue(databaseValues, null); if (currentValue == null || string.IsNullOrEmpty(currentValue.ToString())) { currentValue = "Empty"; } filterContext.Controller.ViewData.ModelState.AddModelError(propertyInfo.Name, "Current value: " + currentValue); } } //TODO: hmm.... how can we only check values applicable to the model/modelstate rather than the entity we saved? //The problem here is we may only have a few fields used in the viewmodel, but many in the entity //so we could have a problem here with that. //object o = propertyInfo.GetValue(myObject, null); } filterContext.ExceptionHandled = true; filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; } }