View Full Version : حرفه ای: تنظیم کلاس BaseEntity جنریک با Fluent Api به صورت سراسری
jaykob
شنبه 30 تیر 1397, 16:18 عصر
با سلام
من در پروژه خودم یک کلاس دارم به شکل زیر :
public abstract class BaseEntity<T>
{
public T Id { get; set; }
public DateTime InsertDate { get; set; }
public DateTime UpdateDate { get; set; }
public string InsertBy { get; set; }
public string UpdateBy { get; set; }
}
و کلاس های من از این کلاس ارث بری می کنند .
خواستم مقادیر این کلاس رو با Fluent api از نظر طول رشته و ... کنترل کنم . خواستم بدونم چطور می تونم یک مرتبه به صورت سراسری این کار رو بکنم ؟ چون داخل فایل Config هر موجودیت جداگانه می شه انجامش دادم ولی برای من مهم اینه که یک مرتبه باشه و کار تکراری انجام نشه
با تشکر
میلاد رئیسی
یک شنبه 31 تیر 1397, 01:13 صبح
A mapped base type means the inheritance hierarchy is represented in the database using either the TPH, TPT or TPC pattern. An unmapped base type means Code First effectively ignores your base type and acts as if the properties defined in the base type were defined on each derived type.
Note: The code in this post is written using EF6 Alpha 2, if you are using a later release you may need to adjust the code to reflect API changes.
Prior to EF6
Prior to EF6 there was no way to configure an unmapped base type. If you wanted to configure a property defined on an unmapped base type you had to explicitly configure it on every derived type.
For example, in the following model we want all types that inherit from EntityBase to use Key as their primary key and to configure ConcurrencyToken as a Rowversion/Timestamp column.
public class EntityBase
{
public int Key { get; set; }
public byte[] ConcurrencyToken { get; set; }
}
public class Blog : EntityBase
{
public string Name { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post : EntityBase
{
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
If we were to configure EntityBase using the fluent API then it would be included in the model and Blog and Post would share a table in the database. The only alternative we are left with is to configure the properties for every type that derives from EntityBase.
public class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasKey(b => b.Key);
modelBuilder.Entity<Blog>().Property(b => b.ConcurrencyToken).IsRowVersion();
modelBuilder.Entity<Post>().HasKey(p => p.Key);
modelBuilder.Entity<Post>().Property(p => p.ConcurrencyToken).IsRowVersion();
}
}
This is an annoying violation of the DRY (Don’t Repeat Yourself) principle… fortunately EF6 comes heralding good news.
EF6 to the Rescue
EF6 introduces the Custom Code First Conventions feature – personally it’s my favorite new feature. I can define a convention that performs a set of configuration for every entity that derives from EntityBase in my model.
modelBuilder.Types<EntityBase>().Configure(c =>
{
c.HasKey(e => e.Key);
c.Property(e => e.ConcurrencyToken).IsRowVersion();
});
I’ve written this convention using ‘lightweight conventions’, this part of the custom conventions feature allows you to build conventions using an API surface that looks and feels similar to the Code First Fluent API. For more information the different types of conventions you can write, check out the walkthrough (http://msdn.microsoft.com/data/jj819164) and feature specification (http://entityframework.codeplex.com/wikipage?title=Custom%20Conventions).
jaykob
یک شنبه 31 تیر 1397, 09:02 صبح
سلام
خیلی ممنون بابت پاسخ خوبتون . فقط سوال من اینه که الان برای ورودی مقدار جنریک کلاس BaseEntity خودم چه ورودی باید بدم ؟ من پروپرتی id رو جنریک گرفتم که بر اساس مدل های خودم int و long و short مقدار id رو در نظر بگیرم .
ممنون می شم این مورد رو توضیح بدید . بر طبق پاسخ شما من کد رو نوشتم فقط همین ورودی رو نمی دونم باید چی بدم .
modelBuilder.Types<BaseEntity<int>>().Configure(c =>
{
c.HasKey(e => e.Id);
c.Property(e => e.InsertBy).HasMaxLength(128).IsRequired();
c.Property(e => e.UpdateBy).HasMaxLength(128);
});
به این صورت که من نوشتم و ورودی رو int دادم فقط روی موجودیت هایی این تنظیمات اعمال می شه که id اونها int باشه . بخواهیم یک چیز کلی بنویسم که برای کلیه Data Type ها کاربرد داشته باشه باید چکار کنیم ؟
تشکر و احترام
میلاد رئیسی
یک شنبه 31 تیر 1397, 19:12 عصر
You can make the method generic:
private static void BasisFields<T>(DbModelBuilder modelBuilder)
where T : BaseEntity
{
modelBuilder.Entity<T>()
.Property(k => k.CreatedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
modelBuilder.Entitity<T>()
.Property(k => k.CreatedDate)
.HasColumnType("datetime");
modelBuilder.Entity<T>()
.Property(k => k.ChangedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
modelBuilder.Entity<T>()
.Property(k => k.ChangedDate)
.HasColumnType("datetime");
}
The generic constraint where T : BaseEntity ensures you can call it only with types that inherit BaseEntity and gives you access to the BaseEntity properties inside the method body.
Now you should call it for every entity which inherits BaseEntity like
BasisFields<DerivedEntityA>(modelBuilder);
BasisFields<DerivedEntityB>(modelBuilder);
...
:لبخند: This is your special section
If you want to make that process automatic for every entity which inherits BaseEntity, you can use the DbModelBuilder.Types<T> method:
Begins configuration of a lightweight convention that applies to all entities and complex types in the model that inherit from or implement the type specified by the generic argument.
So instead of writing a method and calling it for every derived entity, you can simply use:
modelBuilder.Types<BaseEntity>().Configure(c =>
{
c.Property(k => k.CreatedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
c.Property(k => k.CreatedDate)
.HasColumnType("datetime");
c.Property(k => k.ChangedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
c.Property(k => k.ChangedDate)
.HasColumnType("datetime");
});
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.