IOC之Unity的使用详解
原文链接:https://www.cnblogs.com/hua66/p/9670639.html
Unity作为Microsoft推出IOC容器,其功能是非常丰富的,其中需要注意的地方也不少。以下是个人在项目中使用Unity的场景并附实例。
一、封装数据访问层
1、定义对象与DB交互接口
1 public interface IBaseService : IDisposable 2 { 3 T FindEntity<T>(int id) where T : class; 4 void AddEntity<T>(T entity) where T : class; 5 Task<int> AddEntities<T>(IEnumerable<T> entities) where T : class; 6 void UpdateEntity<T>(T entity) where T : class; 7 Task<int> UpdateEntities<T>(IEnumerable<T> entities) where T : class; 8 void DeleteEntity<T>(int id) where T : class; 9 void DeleteEntity<T>(T entity) where T : class; 10 Task<int> DeleteEntities<T>(IEnumerable<T> entities) where T : class; 11 Task<int> DeleteEntities<T>(Func<T, bool> whereLambda) where T : class; 12 IQueryable<T> LoadEntities<T>() where T : class; 13 IQueryable<T> LoadEntities<T>(Func<T, bool> whereLambda) where T : class; 14 IQueryable<T> LoadEntities<T>(Func<T, bool> whereLambda, int pageSize, int pageIndex, out int count) where T : class; 15 IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class; 16 void Excute<T>(string sql, SqlParameter[] parameters) where T : class; 17 int Commit(); 18 Task<int> CommitAsync(); 19 20 }
2、用EF实现数据访问层接口
1 public abstract class EntityService : IBaseService 2 { 3 protected DbContext DBContext { get; private set; } 4 public EntityService(DbContext dbContext) 5 { 6 this.DBContext = dbContext; 7 } 8 public T FindEntity<T>(int id) where T : class 9 { 10 T entity = this.DBContext.Set<T>().Find(id); 11 return entity; 12 } 13 public void AddEntity<T>(T entity) where T : class 14 { 15 this.DBContext.Entry(entity).State = EntityState.Added; 16 } 17 18 [Obsolete("不建议使用基于EF实现批量操作函数,此操作已异步提交!", false)] 19 public Task<int> AddEntities<T>(IEnumerable<T> entities) where T : class 20 { 21 this.DBContext.Set<T>().AddRange(entities); 22 return this.CommitAsync(); 23 } 24 public void UpdateEntity<T>(T entity) where T : class 25 { 26 this.DBContext.Entry(entity).State = EntityState.Modified; 27 //this.DBContext.Set<T>().Attach(entity); 28 //this.DBContext.Entry(entity).Property("") 29 } 30 31 [Obsolete("不建议使用基于EF实现批量操作函数,此操作已异步提交!", false)] 32 public Task<int> UpdateEntities<T>(IEnumerable<T> entities) where T : class 33 { 34 this.DBContext.Configuration.AutoDetectChangesEnabled = false; 35 foreach (var entity in entities) 36 { 37 this.DBContext.Entry(entity).State = EntityState.Modified; 38 } 39 this.DBContext.Configuration.AutoDetectChangesEnabled = true; 40 //this.DBContext.Entry(entities).State= EntityState.Modified; 41 return this.CommitAsync(); 42 } 43 public void DeleteEntity<T>(int id) where T : class 44 { 45 46 T entity = this.DBContext.Set<T>().Find(id); 47 this.DBContext.Set<T>().Remove(entity); 48 } 49 public void DeleteEntity<T>(T entity) where T : class 50 { 51 this.DBContext.Set<T>().Attach(entity); 52 this.DBContext.Entry(entity).State = EntityState.Deleted; 53 } 54 55 [Obsolete("不建议使用基于EF实现批量操作函数,此操作已异步提交!", false)] 56 public Task<int> DeleteEntities<T>(IEnumerable<T> entities) where T : class 57 { 58 //this.DBContext.Configuration.AutoDetectChangesEnabled = false; 59 //foreach (var entity in entities) 60 //{ 61 // this.DBContext.Entry(entity).State = EntityState.Deleted; 62 //} 63 //this.DBContext.Configuration.AutoDetectChangesEnabled = true; 64 this.DBContext.Set<T>().RemoveRange(entities); 65 return this.CommitAsync(); 66 } 67 68 [Obsolete("不建议使用基于EF实现批量操作函数,此操作已异步提交!", false)] 69 public Task<int> DeleteEntities<T>(Func<T, bool> whereLambda) where T : class 70 { 71 IQueryable<T> entities = this.DBContext.Set<T>().Where(whereLambda).AsQueryable(); 72 //this.DBContext.Entry(entities).State = EntityState.Deleted; 73 this.DBContext.Set<T>().RemoveRange(entities); 74 return this.CommitAsync(); 75 } 76 public IQueryable<T> LoadEntities<T>() where T : class 77 { 78 return this.DBContext.Set<T>(); 79 } 80 public IQueryable<T> LoadEntities<T>(Func<T, bool> whereLambda) where T : class 81 { 82 return this.DBContext.Set<T>().Where<T>(whereLambda).AsQueryable(); 83 } 84 public IQueryable<T> LoadEntities<T>(Func<T, bool> whereLambda, int pageSize, int pageIndex, out int count) where T : class 85 { 86 count = 0; 87 count = this.DBContext.Set<T>().Where(whereLambda).AsQueryable().Count(); 88 //return this.DBContext.Set<T>().Where<T>(whereLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsQueryable(); 89 return this.DBContext.Set<T>().Where(whereLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsQueryable(); 90 } 91 92 #region 93 public virtual int Commit() 94 { 95 return this.DBContext.SaveChanges(); 96 } 97 public virtual Task<int> CommitAsync() 98 { 99 return this.DBContext.SaveChangesAsync(); 100 } 101 #endregion 102 public virtual void Dispose() 103 { 104 if (this.DBContext!=null) 105 { 106 this.DBContext.Dispose(); 107 } 108 } 109 110 public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class 111 { 112 return this.DBContext.Database.SqlQuery<T>(sql, parameters).AsQueryable(); 113 } 114 115 public void Excute<T>(string sql, SqlParameter[] parameters) where T : class 116 { 117 DbContextTransaction trans = null; 118 try 119 { 120 trans = this.DBContext.Database.BeginTransaction(); 121 this.DBContext.Database.ExecuteSqlCommand(sql, parameters); 122 trans.Commit(); 123 } 124 catch (Exception ex) 125 { 126 if (trans != null) 127 trans.Rollback(); 128 throw ex; 129 } 130 } 131 }
二、封装Unity容器对象
1 public class ContainerFactory 2 { 3 private static IUnityContainer _iUnityContainer = null; 4 private ContainerFactory() 5 { 6 7 } 8 static ContainerFactory() 9 { 10 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); 11 fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config.xml");//找配置文件的路径 12 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 13 UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); 14 15 _iUnityContainer = new UnityContainer(); 16 section.Configure(_iUnityContainer, "TestContainer"); 17 } 18 19 public static IUnityContainer GetContainerInstance() 20 { 21 return _iUnityContainer; 22 } 23 }
上面使用静态对象方式读取Unity的配置文件并返回Unity的容器对象,方便其他对象使用。
三、示例
1、用Code First初始化DB
1.1、Models
1 [TableAttribute("Base_Sys_User")] 2 public class User 3 { 4 [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 5 public int ID { get; set; } 6 7 [Column("Account")] 8 [StringLength(50)] 9 public String Code { get; set; } 10 11 [StringLength(20)] 12 public String Name { get; set; } 13 14 public Int16? Status { get; set; } 15 public DateTime CreateTime { get; set; }
1.2、填充数据
1 public class DataInitEF :DropCreateDatabaseIfModelChanges<DAL.DBContext.DBContext> 2 { 3 protected override void Seed(DAL.DBContext.DBContext context) 4 { 5 #region 6 SByte? status = 1; 7 User userEntity = new User() 8 { 9 Code = "admin", 10 Name = "管理员", 11 Status = status, 12 CreateTime = DateTime.Now 13 }; 14 15 List<User> users = new List<User>(); 16 17 for (int i = 1; i <= 1000; i++) 18 { 19 if (i % 7 == 0) 20 { 21 status = 2; 22 } 23 else if (i % 17 == 0) 24 { 25 status = 3; 26 } 27 else if (i % 27 == 0) 28 { 29 status = -1; 30 } 31 else 32 { 33 status = 1; 34 } 35 users.Add(new User() 36 { 37 Code = "user_" + i, 38 Name = "user_" + i, 39 Status = status, 40 CreateTime=DateTime.Now 41 }); 42 } 43 context.User.Add(userEntity); 44 context.User.AddRange(users); 45 #endregion 46 47 base.Seed(context); 48 } 49 50 }
2、封装Model服务对象
1 public interface IUserService: IBaseService 2 { 3 UserPayResult Pay( string userAccount, decimal? payment); 4 } 5 6 public class UserService : EntityService, IUserService 7 { 8 [InjectionConstructor]//构造函数注入 9 public UserService(DbContext dbContext) : base(dbContext) 10 { 11 12 } 13 14 15 public UserPayResult Pay( string userAccount, decimal? payment) 16 { 17 UserPayResult result = null; 18 if (!string.IsNullOrEmpty(userAccount)) 19 { 20 result = new UserPayResult() 21 { 22 UserAccount = userAccount, 23 Message = string.Format("{0}:本次成功消费{1}元!", userAccount, payment), 24 Status = 200, 25 RecTimeSpan = DateTime.Now 26 }; 27 } 28 else 29 { 30 result = new UserPayResult() 31 { 32 UserAccount = userAccount, 33 Message = "支付对象余额不足!", 34 Status = 500, 35 RecTimeSpan = DateTime.Now 36 }; 37 } 38 return result; 39 } 40 }
上面封装了User的服务对象,并提供了接口的实现。
3、测试
1 static void UserTest() 2 { 3 { 4 IUnityContainer unityContainer = ContainerFactory.GetContainerInstance(); 5 6 7 IUserService userService = unityContainer.Resolve<IUserService>(); 8 9 int inflCount = 0; 10 var listUser = userService.LoadEntities<User>(); 11 inflCount = listUser.Count(); 12 13 14 userService.Dispose(); 15 } 16 }
可以看到上面并没有具体的细节对象,使用的IUserService接口是通过容器注入的UserService对象,而UserService对象则继承了EntityService对象。这些注入动作通过ContainerFactory对象读取配置文件就已经完成。
四、配置文件
<configuration> <configSections> <!--configSections节点必须放在首位--> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension,Unity.Interception.Configuration"/> <containers> <container name="TestContainer"> <!--AOP扩展--> <extension type="Interception"/> <!-- 完整的类型名称,程序集名称 --> <register type="System.Data.Entity.DbContext, EntityFramework" mapTo="DAL.DBContext.DBContext,DAL" /> <register type="DAL.Service.Interface.IBaseService, DAL" mapTo="DAL.Service.Class.EntityService, DAL" name="DAL.Service.Class.EntityService" /> <register type="UI.Console.UI.Test.IUserService, UI.Console" mapTo="UI.Console.UI.Test.UserService, UI.Console" > <interceptor type="InterfaceInterceptor"/><!--只能对接口做拦截,好处是只要目标类型实现了指定接口就可以拦截--> <interceptionBehavior type="Common.AOP.ExceptionLoggingBehavior, Common"/> <lifetime type="transient" /><!--生命周期--> <constructor> <!--构造函数参数--> <param name="dbContext" type="System.Data.Entity.DbContext, EntityFramework"/> </constructor> </register> </container> </containers> </unity> </configuration>
配置文件格式:<register type="需要注入的类型, 程序集" mapTo="被注入的类型, 程序集" />
五、AOP实现
1 namespace Common.AOP 2 { 3 /// <summary> 4 /// Unity为我们提供了一个IInterceptionBehavior接口需要实现这个接口 5 /// 接口为我们提供了三个方式(GetRequiredInterfaces、Invoke、WillExecute)实现 6 /// WillExecute表示是否执行该行为,如果是false这个方法被调用时,不会被捕捉。因为我们总是要执行的,所以为true 7 /// GetRequiredInterfaces将你想要的接口类型和行为联系起来,我们暂时不需要,所以返回Type.EmptyTypes 8 /// Invoke执行方式接口 9 /// </summary> 10 public class ExceptionLoggingBehavior : IInterceptionBehavior 11 { 12 public bool WillExecute 13 { 14 get { return true; } 15 } 16 17 public IEnumerable<Type> GetRequiredInterfaces() 18 { 19 return Type.EmptyTypes; 20 } 21 22 /// <summary> 23 /// 拦截函数 24 /// </summary> 25 /// <param name="input"></param> 26 /// <param name="getNext"></param> 27 /// <returns></returns> 28 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 29 { 30 string info = string.Empty; 31 info = string.Format("当前函数对象:{0},返回类型:{1}", input.MethodBase.Name, ((MethodInfo)(input.MethodBase)).ReturnType.FullName); 32 33 try 34 { 35 var methodReturn = getNext().Invoke(input, getNext); 36 if (methodReturn.Exception != null) 37 { 38 info += string.Format(" 执行失败:{0}", methodReturn.Exception.Message); 39 } 40 else 41 { 42 info += string.Format(" 执行成功!"); 43 } 44 45 Console.WriteLine(info); 46 47 return methodReturn; 48 } 49 catch (Exception ex) 50 { 51 info += string.Format(" 执行失败:{0}", ex.Message); 52 Console.WriteLine(info); 53 throw ex; 54 } 55 } 56 } 57 }
上面的配置文件中为IUserService接口注入了UserService对象,并且配置AOP对象,所以每当调用UserService对象的方法都会被ExceptionLoggingBehavior对象拦截。