這是一種新的開發模式,注入開發模式,或者叫它IOC模式,說起IOC你可以這樣去理解它,它為你的某個實現流出一個注入點,你生產的對象,可以根據你之前的配置進行組合。 IOC全稱是Inversion of Control,即反轉控制,或者說是依賴注入更為合適。選擇別糾結這些全稱的專業詞。我們可以用別外一些方式去理解它,IOC,是一種設計模式。它的延生所要實現的是把藕合從代碼中移出去,統一放到XML文件中,通過一個容器在需要的時候把這個依賴關系形成,即把需要的接口實現注入到需要它的類中,這可能就是“依賴注入”說法的來源了。
而注入點的位置及方式也是多種多樣的,我們今天主要說一個通過HTTP請求進行注入的方式,IOC工具使用高效的Autofac,對它的認識你可以看這篇文章。
首先看一下我們這個項目的知識點:
MVC4一個對UI層進行分層的架構模式,在微軟的MVC4中加入了開源的Razor引擎
EF(Entity Framework)這無疑是微軟自己比較成功的ORM(Object Relational Mapping)工具(即對象關系映射,目前數據庫是關系型數據庫ORM 主要是把數據庫中的關系數據映射稱為程序中的對象),它執行效率上要高於linq to sql,甚至你自己編寫的ado.net腳本。
Autofac這是在orchard項目中被廣泛的IOC工具,它支持類型,反泛,HTTP等注入
對於這個系統的autofac部分,我們將它的注入點放在controller的構造函數中,將生產的對象配置在global中,當然,你也可以設置在config文件,或者你自己的文件中。
我認為它的工作方式應該是:
網站啟動=>從global中得到ioc配置信息=>http request請求頁面=>通過controller中的參數進行實現的創建=>action中使用創建好的對象
OK,有了這樣一個理論基礎,我們來看一下代碼吧:
EF部分的DATA層
1 public partial class EfRepository<T> : IRepository<T> where T : class 2 { 3 private readonly Guid _instanceId; 4 private readonly IDbContext _context; 5 private IDbSet<T> _entities; 6 public Guid InstanceId 7 { 8 get { return _instanceId; } 9 } 10 public EfRepository(IDbContext context) 11 { 12 AutoCommitEnabled = true; 13 this._context = context; 14 _instanceId = Guid.NewGuid(); 15 } 16 public T GetById(object id) 17 { 18 return this._entities.Find(id); 19 } 20 public T Create() 21 { 22 return this.Entities.Create(); 23 } 24 public void Insert(T entity) 25 { 26 try 27 { 28 if (entity == null) 29 throw new ArgumentNullException("entity"); 30 this.Entities.Add(entity); 31 if (this.AutoCommitEnabled) 32 _context.SaveChanges(); 33 } 34 catch(DbEntityValidationException dbEx) 35 { 36 var msg = string.Empty; 37 foreach(var validationErrors in dbEx.EntityValidationErrors) 38 foreach (var validationError in validationErrors.ValidationErrors) 39 msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine; 40 var fail = new Exception(msg, dbEx); 41 throw fail; 42 } 43 } 44 45 public void InsertRange(IEnumerable<T> entities, int batchSize = 100) 46 { 47 try 48 { 49 if (entities == null) 50 throw new ArgumentNullException("entities"); 51 if (entities.HasItems()) 52 { 53 if (batchSize <= 0) 54 { 55 entities.Each(x => this.Entities.Add(x)); 56 if (this.AutoCommitEnabled) 57 _context.SaveChanges(); 58 } 59 else 60 { 61 int i = 1; 62 bool saved = false; 63 foreach (var entity in entities) 64 { 65 this.Entities.Add(entity); 66 saved = false; 67 if (i % batchSize == 0) 68 { 69 if (this.AutoCommitEnabled) 70 _context.SaveChanges(); 71 i = 0; 72 saved = true; 73 } 74 i++; 75 } 76 if (!saved) 77 { 78 if (this.AutoCommitEnabled) 79 _context.SaveChanges(); 80 } 81 } 82 } 83 } 84 catch (DbEntityValidationException ex) 85 { 86 throw ex; 87 } 88 } 89 public void Update(T entity) 90 { 91 if (entity == null) 92 throw new ArgumentNullException("entity"); 93 try 94 { 95 if (this.InternalContext.Entry(entity).State == EntityState.Detached) 96 { 97 var set = Entities; 98 T attachedEntity = set.Find(entity.Key); // You need to have access to key 99 100 if (attachedEntity != null) 101 { 102 var attachedEntry = this.InternalContext.Entry(attachedEntity); 103 attachedEntry.CurrentValues.SetValues(entity); 104 } 105 else 106 { 107 // This should attach entity 108 this.InternalContext.Entry(entity).State = EntityState.Modified; 109 } 110 } 111 } 112 finally { } 113 114 if (AutoCommitEnabled) 115 _context.SaveChanges(); 116 } 117 118 public void Delete(T entity) 119 { 120 if (entity == null) 121 throw new ArgumentNullException("entity"); 122 if(InternalContext.Entry(entity).State==EntityState.Detached) 123 { 124 this.Entities.Attach(entity); 125 } 126 this.Entities.Remove(entity); 127 if (this.AutoCommitEnabled) 128 _context.SaveChanges(); 129 } 130 131 public IDictionary<string, object> GetModifiedProperties(T entity) 132 { 133 var props = new Dictionary<string, object>(); 134 135 var ctx = InternalContext; 136 var entry = ctx.Entry(entity); 137 var modifiedPropertyNames = from p in entry.CurrentValues.PropertyNames 138 where entry.Property(p).IsModified 139 select p; 140 foreach (var name in modifiedPropertyNames) 141 { 142 props.Add(name, entry.Property(name).OriginalValue); 143 } 144 145 return props; 146 } 147 public virtual IQueryable<T> Table 148 { 149 get 150 { 151 return this.Entities; 152 } 153 } 154 155 public int Commit() 156 { 157 try 158 { 159 return this._context.SaveChanges(); 160 } 161 catch(DbEntityValidationException dbEx) 162 { 163 var msg = string.Empty; 164 foreach (var validationErrors in dbEx.EntityValidationErrors) 165 foreach (var validationError in validationErrors.ValidationErrors) 166 msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); 167 168 var fail = new Exception(msg, dbEx); 169 throw fail; 170 } 171 } 172 #region Helpers 173 protected internal dwDbContextBase InternalContext 174 { 175 get { return _context as dwDbContextBase; } 176 } 177 public bool AutoCommitEnabled { get; set; } 178 179 private DbSet<T> Entities 180 { 181 get 182 { 183 if (_entities == null) { _entities = _context.Set<T>(); } 184 return _entities as DbSet<T>; 185 } 186 } 187 188 #endregion 189 190 public IQueryable<T> Include(IQueryable<T> query,string path) 191 { 192 Guard.ArgumentNotNull(query, "query"); 193 Guard.ArgumentNotEmpty(path, "path"); 194 return query.Include(path); 195 } 196 public IQueryable<T> Include<TProperty>(IQueryable<T> query,Expression<Func<T,TProperty>> path) 197 { 198 Guard.ArgumentNotNull(query, "query"); 199 Guard.ArgumentNotNull(path, "path"); 200 return query.Include(path); 201 } 202 public IDbContext Context 203 { 204 get { return _context; } 205 } 206 207 }
Services層(BLL層)核心代碼:
1 public class TestService : BaseService, ITestService 2 { 3 private const string IncludeProperties = ""; 4 private readonly IRepository<Tests> _repository; 5 private readonly ICacheManager _cacheManager; 6 private readonly IExporter _exporter; 7 public IList<Tests> GetAll() 8 { 9 return _repository.FindBy(null, null, IncludeProperties).ToList(); 10 } 11 12 public TestService(IRepository<Tests> repository, 13 ICacheManager cacheManager,IExporter exporter, 14 IEventPublisher eventPublisher):base(eventPublisher) 15 { 16 _repository = repository; 17 _exporter = exporter; 18 _cacheManager = cacheManager; 19 } 20 21 }
WEB層MVC部分代碼:(注意:我們的WEB層不應該有對DATA層的引用,WEB層一般只注入SERVICE的對象,這一點是需要注意的,即不要直接調用數據庫倉庫)
public class TestController : ApiController { private readonly ITestService _service; private readonly Func<Tests, TestsModel, TestsModel> _convertCallback = (entity, model) => { return model; }; public TestController(ITestService service) { _service = service; } public IEnumerable<TestsModel> Get() { var items = _service.GetAll(); var model = items.ConvertTo(_convertCallback); return model; }
而注入參數我們放在global中,看一下核心代碼:
//初始化依賴注入組件 var bootStrapper = new AutofacBootStrapper(); BootStrapperManager.Initialize(bootStrapper); //設置依賴注入 GlobalConfiguration.Configuration.DependencyResolver = bootStrapper.GetWebApiDependencyResolver(); DependencyResolver.SetResolver(bootStrapper.GetMvcDependencyResolver());
builder.Register(c => new dwObjectContext("MyConnection")).As<IDbContext>().InstancePerHttpRequest().InstancePerApiRequest(); builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest().InstancePerApiRequest(); //注冊緩存類型 builder.RegisterType<StaticCache>().As<ICache>().Named<ICache>(YB_CACHE_STATIC).SingleInstance(); builder.RegisterType<RequestCache>().As<ICache>().Named<ICache>(YB_CACHE_PER_REQUEST).InstancePerHttpRequest().InstancePerApiRequest(); //注冊緩存控制類型 builder.RegisterType<NullCacheManager>() .As<ICacheManager>() .Named<ICacheManager>(YB_SM_CACHE_NULL) .WithParameter(ResolvedParameter.ForNamed<ICache>(YB_CACHE_STATIC)) .SingleInstance(); builder.RegisterType<DefaultCacheManager>() .As<ICacheManager>() .Named<ICacheManager>(YB_SM_CACHE_PER_REQUEST) .WithParameter(ResolvedParameter.ForNamed<ICache>(YB_CACHE_PER_REQUEST)) .InstancePerHttpRequest().InstancePerApiRequest(); builder.RegisterType<DefaultCacheManager>() .As<ICacheManager>() //.Named<ICacheManager>(YB_SM_CACHE_STATIC) .WithParameter(ResolvedParameter.ForNamed<ICache>(YB_CACHE_STATIC)) .SingleInstance(); var assembly = Assembly.GetExecutingAssembly(); //注冊所有 MVC 控制器 builder.RegisterControllers(assembly) .InstancePerHttpRequest(); //注冊所有 Web Api 控制器 builder.RegisterApiControllers(assembly) .InstancePerApiRequest();