مدیریت تغییرات گریدی از اطلاعات به کمک استفاده از الگوی واحد کار مشترک بین ViewModel و لایه سرویس
public User SelectedUser { get; set; }
SelectedItem="{Binding SelectedUser}"
UsersList.Remove(SelectedUser);
[RegularExpression(@"^[\u0600-\u06FF,\u0590-\u05FF,0-9\s]*$", ErrorMessage = "لطفا تنها از اعداد و حروف فارسی استفاده نمائید")] public string FriendlyName { get; set; }
EF Code First #9
public virtual ICollection<Order> Orders { get ; set ; }
میشه بفرمائید برای مقادیر مالی به ریال و تومان بهترین نوع داده ای چیست؟
من اینچنین استفاده میکنم:
public virtual decimal CostPrice { set; get; }
this.Property(x => x.CostPrice) .HasColumnType("money") .IsRequired();
وهله سازی یک کلاس موجود توسط Reflection.Emit
کدهای کامل مثالی را در این زمینه در ادامه ملاحظه میکنید:
using System; using System.Reflection.Emit; namespace FastReflectionTests { public class Order { public string Name { set; get; } public Order() { Name = "Order01"; } } class Program { static void Main(string[] args) { var myMethod = new DynamicMethod(name: "myMethod", returnType: typeof(Order), parameterTypes: Type.EmptyTypes, m: typeof(Program).Module); var il = myMethod.GetILGenerator(); il.Emit(OpCodes.Newobj, typeof(Order).GetConstructor(Type.EmptyTypes)); il.Emit(OpCodes.Ret); var getOrderMethod = (Func<Order>)myMethod.CreateDelegate(typeof(Func<Order>)); Console.WriteLine(getOrderMethod().Name); } } }
سپس با دسترسی به ILGenerator سعی خواهیم کرد تا وهله جدیدی را از کلاس Order ایجاد کنیم. برای این منظور باید از OpCode جدیدی به نام Newobj استفاده کنیم که مخفف new object است. این OpCode برای عملکرد خود، نیاز به دریافت اشارهگری به سازنده کلاسی دارد که قرار است آنرا وهله سازی کند. در اینجا با Ret، کار متد را خاتمه داده و در ادامه برای استفاده از آن تنها کافی است یک delegate را ایجاد نمائیم.
بنابراین به مجموعه متدهای سریع خود، متد ذیل را نیز میتوان افزود:
public static Func<T> CreatFastObjectInstantiater<T>() { var t = typeof(T); var ctor = t.GetConstructor(Type.EmptyTypes); if (ctor == null) return null; var dynamicCtor = new DynamicMethod("_", t, Type.EmptyTypes, t, true); var il = dynamicCtor.GetILGenerator(); il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Ret); return (Func<T>)dynamicCtor.CreateDelegate(typeof(Func<T>)); }
اگر علاقمند بودید که سرعت این روش را با روش متداول Activator.CreateInstance مقایسه کنید، مطلب زیر بسیار مفید است:
Creating objects - Perf implications
یک کاربرد مهم این مساله در نوشتن ORM مانندهایی است که قرار است لیستی جنریک را خیلی سریع تولید کنند؛ از این جهت که در حلقه DataReader آنها مدام نیاز است یک وهله جدید از شیء مدنظر ایجاد و مقدار دهی شود:
Mapping Datareader to Objects Using Reflection.Emit
public class Service { public int ServiceId { get; set; } public string ServiceName { get; set; } }
public interface ICoreService { Service LoadDefaultService(); }
An unhandled exception occurred while processing the request InvalidOperationException: Unable to resolve service for type 'WebApplication1.Models.ICoreService' while attempting to activate 'WebApplication1.Controllers.HomeController' Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
در نسخههای قدیمی MVC (منظور نسخههای قبل از 6)، برای تزریق وابستگیها از یک Controller Factory یا Dependency Resolver سفارشی استفاده میشد. اما در نسخه جدید MVC دیگری خبری از روشهای قدیمی نیست. چونکه یک سیستم تزریق وابستگی توکار، همراه با MVC یکپارچه شدهاست که عملیات تزریق وابستگیها را انجام میدهد. سیستم تزریق وابستگی پیش فرض، تنها از 4 حالت عملیاتی پشتیبانی میکند:
تیم Asp.Net برای فراهم آوردن امکان تزریق وابستگیها، تصمیم به انتزاعی کردن ویژگیهای مشترک محبوبترین Ioc Containerها و اجازه دادن به میان افزارها، جهت ارتباط با این اینترفیسها برای دستیابی به تزریق وابستگی بود.
namespace Microsoft.Extensions.DependencyInjection { // // Summary: // Specifies the lifetime of a service in an Microsoft.Extensions.DependencyInjection.IServiceCollection. public enum ServiceLifetime { // // Summary: // Specifies that a single instance of the service will be created. Singleton = 0, // // Summary: // Specifies that a new instance of the service will be created for each scope. // // Remarks: // In ASP.NET Core applications a scope is created around each server request. Scoped = 1, // // Summary: // Specifies that a new instance of the service will be created every time it is // requested. Transient = 2 } }
public void ConfigureServices(IServiceCollection services) { ServiceDescriptor descriptor = new ServiceDescriptor(typeof(ICoreService),typeof(CoreServise),ServiceLifetime.Transient); services.Add(descriptor); services.AddMvc(); }
ساخت یک Service Descriptor و اضافه کردن آن به سرویسها، فلسفه وجودی میان افزارها را زیر سوال میبرد. پس بجای ایجاد یک Service Descriptor، از متدهای الحاقی تدارک دیده شده استفاده میکنیم. مثلا بجای دو خط کد بالا میتوان از کد زیر استفاده نمود:
services.AddTransient<ICoreService,CoreServise>();
حال که یک دید کلی از نحوه کار مکانیزم تزریق وابستگی بدست آوردیم، میخواهیم این مکانیزم را با StructureMap جایگزین کنیم. بدین منظور ابتدا پکیج StructureMap را نصب میکنم.
در مرحله اول باید کلاسهایی را تدارک ببینیم که اینترفیسهای بالا را پیاده سازی نمایند. یعنی کلاسهای ما باید بتوانند همان کاری را انجام دهند که مکانیزم پیش فرض MVC انجام میدهد.
اولین مورد، کلاس StructureMapServiceProvider میباشد.
internal class StructureMapServiceProvider : IServiceProvider { private readonly IContainer _container; public StructureMapServiceProvider(IContainer container, bool scoped = false) { _container = container; } public object GetService(Type type) { try { return _container.GetInstance(type); } catch { return null; } } }
مورد دوم کلاس StructureMapServiceScope میباشد:
internal class StructureMapServiceScope : IServiceScope { private readonly IContainer _container; private readonly IContainer _childContainer; private IServiceProvider _provider; public StructureMapServiceScope(IContainer container) { _container = container; _childContainer = _container.GetNestedContainer(); _provider = new StructureMapServiceProvider(_childContainer, true); } public IServiceProvider ServiceProvider => _provider; public void Dispose() { _provider = null; if (_childContainer != null) _childContainer.Dispose(); } }
مورد سوم StructureMapServiceScopeFactory میباشد:
internal class StructureMapServiceScopeFactory : IServiceScopeFactory { private IContainer _container; public StructureMapServiceScopeFactory(IContainer container) { _container = container; } public IServiceScope CreateScope() { return new StructureMapServiceScope(_container); } }
مورد بعدی کلاس StructureMapPopulator میباشد. وظیفه این کلاس جمع آوری اطلاعات مربوط به سرویسها میباشد.
internal class StructureMapPopulator { private IContainer _container; public StructureMapPopulator(IContainer container) { _container = container; } public void Populate(IEnumerable<ServiceDescriptor> descriptors) { _container.Configure(c => { c.For<IServiceProvider>().Use(new StructureMapServiceProvider(_container)); c.For<IServiceScopeFactory>().Use<StructureMapServiceScopeFactory>(); foreach (var descriptor in descriptors) { switch (descriptor.Lifetime) { case ServiceLifetime.Singleton: Use(c.For(descriptor.ServiceType).Singleton(), descriptor); break; case ServiceLifetime.Transient: Use(c.For(descriptor.ServiceType), descriptor); break; case ServiceLifetime.Scoped: Use(c.For(descriptor.ServiceType), descriptor); break; } } }); } private static void Use(GenericFamilyExpression expression, ServiceDescriptor descriptor) { if (descriptor.ImplementationFactory != null) { expression.Use(Guid.NewGuid().ToString(), context => { return descriptor.ImplementationFactory(context.GetInstance<IServiceProvider>()); }); } else if (descriptor.ImplementationInstance != null) { expression.Use(descriptor.ImplementationInstance); } else if (descriptor.ImplementationType != null) { expression.Use(descriptor.ImplementationType); } else { throw new InvalidOperationException("IServiceDescriptor is invalid"); } } }
و در آخر کلاس StructureMapRegistration میباشد:
public static class StructureMapRegistration { public static void Populate(this IContainer container, IEnumerable<ServiceDescriptor> descriptors) { var populator = new StructureMapPopulator(container); populator.Populate(descriptors); } }
نهایتاً باید متد ConfigurationServices در کلاس StartUp را اندکی تغییر دهیم.
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); var container = new Container(); container.Configure(configure => { configure.For<ICoreService>().Use<CoreServise>(); }); container.Populate(services); return container.GetInstance<IServiceProvider>(); }
در کد بالا، متد ConfigurationServices به جای آنکه Void برگرداند، نمونهای از اینترفیس IServiceProvider را برمیگرداند. حال اگر برنامه را اجرا کنیم، وابستگیها توسط StructureMap تزریق شده و برنامه بدون هیچ مشکلی اجرا میشود.
@using Microsoft.Extensions.Logging @inject ILogger<Error> Logger @inject IJSRuntime jsRuntime <CascadingValue Value="this"> @ChildContent </CascadingValue> @code { [Parameter] public RenderFragment? ChildContent { get; set; } public void ProcessError(Exception ex) { Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}", ex.GetType(), ex.Message); jsRuntime.ToastrError("متاسفانه خطایی رخ داد"); //StateHasChanged(); } }
<Error> <Router ...> ... </Router> </Error>
@code { [CascadingParameter] public Error? Error { get; set; } private void CreatePost() { try { throw new InvalidOperationException("پست ساخته نشد!"); } catch (Exception ex) { Error?.ProcessError(ex); } } }