(2)在DbSettings中按需定义依赖,这里将实体类的配置也通过DbSettings注入。
1 namespace Example.Infrastructure.Repository
2 {
3     public class DbSettings
4     {
5         public DbSettings()
6         {
7             this.RowVersionNname = "Version";
8         }
9
10         public string NameOrConnectionString { get; set; }
11
12         public string RowVersionNname { get; set; }
13         public bool Debug { get; set; }
14
15         public bool UnitTest { get; set; }
16
17         public IDbConnectionFactory DbConnectionFactory { get; set; }
18
19         public List<object> EntityMaps { get; set; } = new List<object>();
20
21         public List<object> ComplexMaps { get; set; } = new List<object>();
22     }
23 }
  3.定义SqlServerDbContext和VersionDbContext,解决使用开放式并发连接时,MySql等数据库无法自动生成RowVersion的问题。
  (1)适用于SqlServer、SqlServeCe的SqlServerDbContext
1 namespace Example.Infrastructure.Repository
2 {
3     public class SqlServerDbContext : EfDbContext
4     {
5         private DbSettings _dbSettings;
6
7         public SqlServerDbContext(IConfiguration configuration, ILogger logger, DbSettings dbSettings)
8             : base(configuration, logger, dbSettings)
9         {
10             this._dbSettings = dbSettings;
11         }
12
13         protected override void OnModelCreating(DbModelBuilder modelBuilder)
14         {
15             base.OnModelCreating(modelBuilder);
16             modelBuilder.Properties().Where(o => o.Name == this._dbSettings.RowVersionNname).Configure(o => o.IsRowVersion());
17             base.SetInitializer<SqlServerDbContext>();
18         }
19     }
20 }
  (2)适用于Myql、Sqlite等数据库的VersionDbContext。使用手动更新Version,通过GUID保证版本号。
1 namespace Example.Infrastructure.Repository
2 {
3     public class VersionDbContext : EfDbContext
4     {
5         private DbSettings _dbSettings;
6
7         public VersionDbContext(IConfiguration configuration, ILogger logger, DbSettings dbSettings)
8             : base(configuration,logger,dbSettings)
9         {
10             this._dbSettings = dbSettings;
11         }
12
13         protected override void OnModelCreating(DbModelBuilder modelBuilder)
14         {
15             base.OnModelCreating(modelBuilder);
16             modelBuilder.Properties().Where(o => o.Name == this._dbSettings.RowVersionNname)
17                 .Configure(o => o.IsConcurrencyToken().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
18             base.SetInitializer<VersionDbContext>();
19         }
20
21         public override int SaveChanges()
22         {
23             this.ChangeTracker.DetectChanges();
24             var objectContext = ((IObjectContextAdapter)this).ObjectContext;
25             foreach (ObjectStateEntry entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added))
26             {
27                 var v = entry.Entity;
28                 if (v != null)
29                 {
30                     var property = v.GetType().GetProperty(this._dbSettings.RowVersionNname);
31                     if (property != null)
32                     {
33                         var value = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
34                         property.SetValue(v, value);
35                     }
36                 }
37             }
38             return base.SaveChanges();
39         }
40     }
41 }
  4.使用XUnit、Rhino.Mocks和SqlServerCe进行单元测试
  这是参考nopcommerce中的做法,nopcommerce使用的NUnit需要安装NUnit扩展,XUnit只需要通过Nuget引入程序包,看看GitHub上的aspnet源码,微软也在使用XUnit。
1 namespace Example.Infrastructure.Test.Repository
2 {
3     public class CustomerPersistenceTest
4     {
5         private IRepository<T> GetRepository<T>() where T : class
6         {
7             string testDbName = "Data Source=" + (System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)) + @"\test.sdf;Persist Security Info=False";
8             var configuration = MockRepository.GenerateMock<IConfiguration>();
9             var logger = MockRepository.GenerateMock<ILogger>();
10             var repository = new EfRepository<T>(new SqlServerDbContext(configuration,logger,new DbSettings
11             {
12                 DbConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"),
13                 NameOrConnectionString = testDbName,
14                 Debug = true,
15                 UnitTest = true,
16                 EntityMaps = new List<object> { new EntityTypeConfiguration<Customer>() }
17             }));
18             return repository;
19         }
20
21         [Fact]
22         public void SaveLoadCustomerTest()
23         {
24             var repository = this.GetRepository<Customer>();
25             repository.Add(new Customer { UserName = "test" });
26             repository.Commit();
27             var customer = repository.Query.FirstOrDefault(o => o.UserName == "test");
28             Assert.NotNull(customer);
29         }
30     }
31 }
  5.确保在ASP.NET中使用依赖注入时,配置DbContext的生命周期为Request范围
1 namespace Example.Web
2 {
3     public class MvcApplication : System.Web.HttpApplication
4     {
5         protected void Application_Start()
6         {
7             ObjectFactory.Init();
8             ObjectFactory.AddSingleton<IConfiguration, AppConfigAdapter>();
9             ObjectFactory.AddSingleton<ILogger, Log4netAdapter>();
10             ObjectFactory.AddSingleton<DbSettings, DbSettings>(new DbSettings { NameOrConnectionString = "SqlCeConnection", Debug = true });
11             ObjectFactory.AddScoped<IDbContext, SqlServerDbContext>();
12             ObjectFactory.AddTransient(typeof(IRepository<>), typeof(EfRepository<>));
13             ObjectFactory.Build();
14             ObjectFactory.GetInstance<ILogger>().Information(String.Format("Start at {0}",DateTime.Now));
15             AreaRegistration.RegisterAllAreas();
16             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
17             RouteConfig.RegisterRoutes(RouteTable.Routes);
18             BundleConfig.RegisterBundles(BundleTable.Bundles);
19         }
20
21         protected void Application_EndRequest()
22         {
23             ObjectFactory.Dispose();
24         }
25     }
26 }
  依赖注入这里采用的是StructureMap。HttpContextLifecycle提供了Request范围内的生命周期管理但未定义在StructureMap程序包中,需要引入StructureMap.Web程序包。使用HttpContextLifecycle时需要在Application_EndRequest调用HttpContextLifecycle.DisposeAndClearAll()方法。