پیاده سازی UnitOfWork به وسیله MEF
نویسنده: مسعود پاکدل
تاریخ: ۱۳۹۱/۱۲/۲۴ ۲۲:۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
} public class CategoryMap : EntityTypeConfiguration<Entity.Category>
{
public CategoryMap()
{
ToTable( "Category" );
HasKey( _field => _field.Id );
Property( _field => _field.Title )
.IsRequired();
}
}using System.Data.Entity;
using System.Data.Entity.Infrastructure;
namespace DataAccess
{
public interface IUnitOfWork
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>() where TEntity : class;
void SaveChanges();
void Dispose();
}
}[InheritedExport( typeof( IUnitOfWork ) )]
public class DatabaseContext : DbContext, IUnitOfWork
{
private DbTransaction transaction = null;
public DatabaseContext()
{
this.Configuration.AutoDetectChangesEnabled = false;
this.Configuration.LazyLoadingEnabled = true;
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.AddFormAssembly( Assembly.GetAssembly( typeof( Entity.Map.CategoryMap ) ) );
}
public DbEntityEntry<TEntity> Entry<TEntity>() where TEntity : class
{
return this.Entry<TEntity>();
}
} public static class ModelBuilderExtension
{
public static void AddFormAssembly( this DbModelBuilder modelBuilder, Assembly assembly )
{
Array.ForEach<Type>( assembly.GetTypes().Where( type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof( EntityTypeConfiguration<> ) ).ToArray(), delegate( Type type )
{
dynamic instance = Activator.CreateInstance( type );
modelBuilder.Configurations.Add( instance );
} );
}
}public class BusinessBase<TEntity> where TEntity : class
{
public BusinessBase( IUnitOfWork unitOfWork )
{
this.UnitOfWork = unitOfWork;
}
[Import]
public IUnitOfWork UnitOfWork
{
get;
private set;
}
public virtual IEnumerable<TEntity> GetAll()
{
return UnitOfWork.Set<TEntity>().AsNoTracking();
}
public virtual void Add( TEntity entity )
{
try
{
UnitOfWork.Set<TEntity>().Add( entity );
UnitOfWork.SaveChanges();
}
catch
{
throw;
}
finally
{
UnitOfWork.Dispose();
}
}
} public class Category : BusinessBase<Entity.Category>
{
[ImportingConstructor]
public Category( [Import( typeof( IUnitOfWork ) )] IUnitOfWork unitOfWork )
: base( unitOfWork )
{
}
}public class Plugin
{
public void Run()
{
AggregateCatalog catalog = new AggregateCatalog();
Container = new CompositionContainer( catalog );
CompositionBatch batch = new CompositionBatch();
catalog.Catalogs.Add( new AssemblyCatalog( Assembly.GetExecutingAssembly() ) );
batch.AddPart( this );
Container.Compose( batch );
}
public CompositionContainer Container
{
get;
private set;
}
} class Program
{
static void Main( string[] args )
{
Plugin plugin = new Plugin();
plugin.Run();
Category category = new Category(plugin.Container.GetExportedValue<IUnitOfWork>());
category.GetAll().ToList().ForEach( _record => Console.Write( _record.Title ) );
}
} protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.AddFormAssembly( Assembly.GetAssembly( typeof( Entity.Map.CategoryMap ) ) );
}مهم نیست. همینقدر که ایده اینکار مطرح شده مابقیاش هنر Reflection مصرف کننده است. مثلا از یک رشته (ذخیره شده در تنظیمات برنامه) هم میشود این نامها را دریافت کرد: Assembly.Load
public class MyBusiness : BusinessBase<Entity.MyEntity>
{
[ImportingConstructor]
public MyBusiness ( [Import( typeof( IUnitOfWork ) )] IUnitOfWork unitOfWork )
: base( unitOfWork )
{
}
public void Add(Entity.MyEntity entity)
{
UnitOfWork.Set<MyEntity>().Add(entity);
}
}public class Category : BusinessBase<Entity.Category>
{
[ImportingConstructor]
public Category( [Import( typeof( IUnitOfWork ) )] IUnitOfWork unitOfWork )
: base( unitOfWork )
{
}
public override void Add( Entity.Category entity )
{
new MyBusiness().Add( new Entity.MyEntity() );
UnitOfWork.Set<Entity.Category>().Add( entity );
UnitOfWork.SaveChanges();
}
}من الان یک ویژگی StringLength به طول 30 رو روی خاصیت Title کلاس Category مقاله جاری اضافه کردم. با همین کدهای فوق، این فیلد با طول 30 الان در دیتابیس قابل مشاهده است. آغاز دیتابیس اصلا کاری به MEF نداره.
این هم فایل مثالی که در آن ویژگی طول رشته اعمال شده برای آزمایش:
public class VegaContext : DbContext, IUnitOfWork, IDbContext
{
#region Constructors (2)
/// <summary>
/// Initializes the <see cref="VegaContext" /> class.
/// </summary>
static VegaContext()
{
Database.SetInitializer<VegaContext>(null);
}
/// <summary>
/// Initializes a new instance of the <see cref="VegaContext" /> class.
/// </summary>
public VegaContext() : base("LocalSqlServer") { }
#endregion Constructors
#region Properties (2)
/// <summary>
/// Gets or sets the languages.
/// </summary>
/// <value>
/// The languages.
/// </value>
public DbSet<Language> Languages { get; set; }
/// <summary>
/// Gets or sets the resources.
/// </summary>
/// <value>
/// The resources.
/// </value>
public DbSet<Resource> Resources { get; set; }
#endregion Properties
#region Methods (2)
// Public Methods (1)
/// <summary>
/// Setups the specified model builder.
/// </summary>
/// <param name="modelBuilder">The model builder.</param>
public void Setup(DbModelBuilder modelBuilder)
{
//todo
modelBuilder.Configurations.Add(new ResourceMap());
modelBuilder.Configurations.Add(new LanguageMap());
modelBuilder.Entity<Resource>().ToTable("Vega_Languages_Resources");
modelBuilder.Entity<Language>().ToTable("Vega_Languages_Languages");
//base.OnModelCreating(modelBuilder);
}
// Protected Methods (1)
/// <summary>
/// This method is called when the model for a derived context has been initialized, but
/// before the model has been locked down and used to initialize the context. The default
/// implementation of this method does nothing, but it can be overridden in a derived class
/// such that the model can be further configured before it is locked down.
/// </summary>
/// <param name="modelBuilder">The builder that defines the model for the context being created.</param>
/// <remarks>
/// Typically, this method is called only once when the first instance of a derived context
/// is created. The model for that context is then cached and is for all further instances of
/// the context in the app domain. This caching can be disabled by setting the ModelCaching
/// property on the given ModelBuidler, but note that this can seriously degrade performance.
/// More control over caching is provided through use of the DbModelBuilder and DbContextFactory
/// classes directly.
/// </remarks>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ResourceMap());
modelBuilder.Configurations.Add(new LanguageMap());
modelBuilder.Entity<Resource>().ToTable("Vega_Languages_Resources");
modelBuilder.Entity<Language>().ToTable("Vega_Languages_Languages");
base.OnModelCreating(modelBuilder);
}
#endregion Methods
#region IUnitOfWork Members
/// <summary>
/// Sets this instance.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns></returns>
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion
} public class LoadContexts
{
public LoadContexts()
{
var directoryPath = HttpRuntime.BinDirectory;//AppDomain.CurrentDomain.BaseDirectory; //"Dll folder path";
var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");
var aggregateCatalog = new AggregateCatalog();
aggregateCatalog.Catalogs.Add(directoryCatalog);
var container = new CompositionContainer(aggregateCatalog);
container.ComposeParts(this);
}
//[Import]
//public IPlugin Plugin { get; set; }
[ImportMany]
public IEnumerable<IDbContext> Contexts { get; set; }
} public class MainContext : DbContext, IUnitOfWork
{
public MainContext() : base("LocalSqlServer") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var contextList = new LoadContexts(); //ObjectFactory.GetAllInstances<IDbContext>();
foreach (var context in contextList.Contexts)
context.Setup(modelBuilder);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MainContext, Configuration>());
//Database.SetInitializer(new DropCreateDatabaseAlways<MainContext>());
}
/// <summary>
/// Sets this instance.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns></returns>
public IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
} - بنابراین روی ساختار دیتابیس تاثیر داره. مثالش هم پیوست شد برای آزمایش.
- عمل نکردن خطاهای اعتبارسنجی به بود و نبود یک سری از تعاریف لازم در View هم بر میگرده. توضیح شما یعنی عمل نکردن اعتبارسنجی سمت کلاینت ولی عمل کردن اعتبارسنجی سمت سرور. به ترتیب باید jquery.min.js ، jquery.validate.min.js و jquery.validate.unobtrusive.min.js به View الحاق شده باشند. تنظیمات ClientValidationEnabled و UnobtrusiveJavaScriptEnabled در وب کانفیگ فعال باشند. از متدهایی مانند ValidationMessageFor استفاده شده باشد. این متدها یک سری ویژگیهای خاص unobtrusive رو به عناصر HTML برای شناسایی توسط jquery.validate اضافه میکنند و بدون اینها عملا اعتبارسنجی سمت کاربر رخ نمیده.