اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
پنج دقیقه
فر ض کنید پروژه بزرگی دارید
که هر قسمت را به یک برنامه نویس میسپارید تا آن قسمت را در پروژه مجزایی طراحی و برنامه نویسی کند. هر برنامه نویس Entityهای خاص خود را در لایههای مربوط به پروژه خود تعریف میکند و از آنها استفاده میکند. حال یکی از برنامه نویسها میخواهد از Entity های پروژه دیگر استفاده کند. در این صورت اگر از دو Context شیءایی را بسازد و آنها را با یکدیگر Join بزند، خطایی مربوط به تعلق داشتن دو Entity به دو Context متفاوت را میگیرد.
و در SampleProject2، مدل ProductType را داریم که هر دو Entity از کلاس Entity ارث بری میکند:
همه پروژهها را در پروژهی SampleProject1.Console، به عنوان رفرنس اضافه میکنیم؛ بجز SampleProject2.Console و Output path همه پروژهها را به یک پوشه مشترک هدایت میکنیم. در ادامه برای بدست آوردن Entityها از کد زیر استفاده میکنیم:
و سپس برای Generate کردن کلاس DbContext از کلاس زیر استفاده میکنیم:
در پروژههای کوچک، کل تیم بر روی ماژولهای مختلف یک پروژه کار میکنند و یک DbContext مشترک دارند. اما راه حل این مشکل در پروژههای بزرگ چیست؟
یکی از راههای پیشنهادی، استفاده از یک کلاس DbContextBase است که همه پروژهها بایستی Context خود را از این کلاس به ارث ببرند که در این صورت باز هم مشکل ساخت چند DbContext وجود خواهد داشت که فقط میتوان از Entityهای موجود در DbContextBase و DbContext پروژه جاری استفاده کرد. اما در شرکتهای بزرگ که پروژههایی مانندERP دارند، روش دیگری استفاده میشود که در ادامه خواهیم دید.
روش مورد استفاده به این صورت است که در زمان اجرا یک DbContext برای همه Entityهای پروژههای مختلف ساخته میشود. اجازه بدهید همراه با مثال، این پروژه را پیش برویم. فرض کنید دو تیم برنامه نویسی داریم که هر کدام بر روی پروژههای مجزای SampleProject1 و SampleProject2 کار میکنند که Entityهای هر کدام در لایههای Common قرار گرفتهاند.
در SampleProject1 مدل Product را داریم:
public partial class Product : Entity { public int Id { get; set; } public string Name { get; set; } public Nullable<byte> ProductTypeId { get; set; } }
public partial class ProductType : Entity { public byte Id { get; set; } public string Name { get; set; } }
List<Assembly> allAssemblies = new List<Assembly>(); string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); foreach (string dll in Directory.GetFiles(path, "*.Common.dll")) allAssemblies.Add(Assembly.LoadFile(dll)); var type = typeof(Entity); List<Type> types = allAssemblies .SelectMany(s => s.GetTypes()) .Where(p => type.IsAssignableFrom(p)).ToList(); List<string> entities = new List<string>(); foreach (var item in types) { entities.Add(item.Name); } types.Add(typeof(Entity));
public class ContextGenerator { public void Generate(List<string> entities, params Type[] types) { StringBuilder code = new StringBuilder(); code.AppendLine(@" using System.Data.Entity; using System.Data.Entity.Core.EntityClient; using SampleProject1.Common.Models; using SampleProject1.Common.Models.Mapping; using SampleProject2.Common.Models; using SampleProject2.Common.Models.Mapping; namespace DbContextGenerator { public partial class TestContext : DbContext { static TestContext() { Database.SetInitializer<TestContext>(null); } public TestContext() : base(""Data Source=.;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True"") { } "); var pluralizeHelper = new PluralizeHelper(); foreach (var entity in entities) { code.AppendLine($@"public DbSet<{entity}> {pluralizeHelper.Pluralize(entity)} {{ get; set; }}"); } code.AppendLine(@"protected override void OnModelCreating(DbModelBuilder modelBuilder)"); code.AppendLine(@"{"); foreach (var entity in entities) { code.AppendLine($@"modelBuilder.Configurations.Add(new {entity}Map());"); } code.AppendLine(@"}"); code.AppendLine(@"}"); code.AppendLine(@"}"); CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add("System.Drawing.dll"); parameters.ReferencedAssemblies.Add("System.Data.dll"); parameters.ReferencedAssemblies.Add("System.Data.Entity.dll"); parameters.ReferencedAssemblies.Add("System.ComponentModel.dll"); foreach (var type in types) { parameters.ReferencedAssemblies.Add(type.Assembly.Location); } parameters.ReferencedAssemblies.Add(typeof(DbSet).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(DbContext).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(IQueryable).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(IQueryable<>).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(System.ComponentModel.IListSource).Assembly.Location); parameters.GenerateExecutable = false; parameters.GenerateInMemory = false; parameters.OutputAssembly = "ProjectContext.dll"; CompilerResults results = provider.CompileAssemblyFromSource(parameters, code.ToString()); if (results.Errors.HasErrors) { StringBuilder sb = new StringBuilder(); foreach (CompilerError error in results.Errors) { sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)); } throw new InvalidOperationException(sb.ToString()); } } }
و نحوه فراخوانی آن:
new ContextGenerator().Generate(entities, types.ToArray()); // generate dbContext
همانطور که مشاهده میکنید، برای تولید کد، از کلاس CSharpCodeProvider استفاده میکنیم که نتیجه اجرای کد بالا، ساخت DLLی به نام ProjectContext.dll است. با مشاهده DLL ساخته شده توسط نرم افزار ILSpy، کد جنریت شده به صورت زیر خواهد بود:
حال برای استفاده از Context تولید شده، به صورت زیر شیءایی را ساخته:
static DbContext _dbContext=null; public static DbContext GetDbContextInstance() { if (_dbContext == null) { string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var dllversionAssm = Assembly.LoadFile(path + "\\ProjectContext.dll"); Type type = dllversionAssm.GetType("DbContextGenerator.TestContext"); _dbContext = (DbContext)Activator.CreateInstance(type); } return _dbContext; }
و سپس برای ساخت DbSet از هر Entity به کد زیر نیاز خواهیم داشت:
public static System.Data.Entity.DbSet<T> Get<T>() where T : class { var set = GetDbContextInstance().Set<T>(); return set; }
هم اکنون میتوان رکوردهای Entityها را واکشی کرده و یا آنها را با یکدیگر Join بزنیم:
var products = Get<Product>().ToList(); var productTypes = Get<ProductType>().ToList(); var query = from p in Get<Product>() join pt in Get<ProductType>() on p.ProductTypeId equals pt.Id select new { Id = p.Id, Name = p.Name, ProductType = pt.Name }; var JoinResult = query.ToList();
و نتیجه واکشی ها