EF实体对象解耦 - 泛型联表查询
为了达到模块间最小耦合,单模块业务数据不与其他模块发生关系。在操作数据库的时候,采用EF泛型操作。但泛型操作不好实现联表,经过一晚的试验发现了一种定义数据库上下文并联表的方式。
1.实体对象定义。实体对象可能存在于不同的业务模块中。他们之间是相互不知道对方存在的。
1 public class User 2 { 3 [Key] 4 [MaxLength(50)] 5 public string userId { get; set; } 6 [MaxLength(50)] 7 public string userName { get; set; } 8 public int age { get; set; } 9 public string sex { get; set; } 10 } 11 12 public class Order 13 { 14 [Key] 15 [MaxLength(50)] 16 public string orderId { get; set; } 17 public DateTime createTime { get; set; } 18 public string userId { get; set; } 19 20 public string goodsId { get; set; } 21 } 22 23 public class Goods 24 { 25 [Key] 26 [MaxLength(50)] 27 public string goodsId { get; set; } 28 public decimal price { get; set; } 29 public float weight { get; set; } 30 }
2.DbContext定义
1 /// <summary> 2 /// 基础的数据库操作类, 3 /// 定义了所有的表结构,定义了数据迁移方案 4 /// </summary> 5 public class DbHelper : DbContext 6 { 7 static List<Type> tList; 8 9 static DbHelper() 10 { 11 //也可以搜索所有程序集里面需要映射表的类型,这样就不需要外部传入了。 12 } 13 14 /// <summary> 15 /// 初始化DB,该方法只需要被调用一次 16 /// 总的说来,必须要在一开始就知道有哪些类型是要进行表映射的。(准确的说,只要在联表调用之前将对应类型在EF中注册过就可以。使用DbHelper<E>会将新的类型注册到EF,即便这个类型没有在此处统一注册★) 17 /// </summary> 18 /// <param name="eTypeList">需要关联的实体类对象</param> 19 public static void InitDbHelper(List<Type> eTypeList=null) 20 { 21 tList = eTypeList ?? tList; 22 using (DbHelper db = new Db.DbHelper()) 23 { 24 try 25 { 26 db.Set<string>().Add(""); 27 } 28 catch (InvalidOperationException ex) 29 { 30 } 31 } 32 33 } 34 35 public DbHelper() : base("defaultConnect") 36 { 37 System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<DbHelper, Configuration<DbHelper>>()); 38 } 39 40 public DbHelper(string connectionName= "defaultConnect") : base(connectionName) 41 { 42 System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<DbHelper, Configuration<DbHelper>>()); 43 } 44 protected override void OnModelCreating(DbModelBuilder modelBuilder) 45 { 46 if(tList != null) 47 { 48 tList.ForEach(f=>{ 49 modelBuilder.RegisterEntityType(f); 50 }); 51 } 52 //modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 53 base.OnModelCreating(modelBuilder); 54 } 55 } 56 57 /// <summary> 58 /// 数据迁移设置 59 /// </summary> 60 /// <typeparam name="T"></typeparam> 61 public class Configuration<T> : DbMigrationsConfiguration<T> where T : DbContext 62 { 63 public Configuration() 64 { 65 AutomaticMigrationsEnabled = true; // 启用自动迁移功能 66 AutomaticMigrationDataLossAllowed = true; // 允许自动删字段,危险但是不加这个不能重命名字段 67 } 68 } 69 public class DbHelper<E> : DbContext where E : class 70 { 71 public DbHelper(string connectionName = "defaultConnect") : base(connectionName) 72 { 73 } 74 75 private DbSet<E> Data { get; set; } 76 } 77 public class DbHelper<E1,E2>:DbContext where E1:class where E2:class 78 { 79 public DbHelper(string connectionName = "defaultConnect") : base(connectionName) 80 { 81 } 82 83 private DbSet<E1> Data1{ get; set; } 84 private DbSet<E2> Data2 { get; set; } 85 86 } 87 88 /// <summary> 89 /// 如果超出了这里定义的实体个数,可以由外部自行定义DbHelper。 90 /// </summary> 91 /// <typeparam name="E1"></typeparam> 92 /// <typeparam name="E2"></typeparam> 93 /// <typeparam name="E3"></typeparam> 94 public class DbHelper<E1,E2,E3> : DbContext where E1 : class where E2 : class where E3:class 95 { 96 public DbHelper(string connectionName = "defaultConnect") : base(connectionName) 97 { 98 } 99 100 private DbSet<E1> Data1 { get; set; } 101 private DbSet<E2> Data2 { get; set; } 102 private DbSet<E3> Data3 { get; set; } 103 104 } 105 }
3.使用和操作。
在应用程序初始化的时候(如:Application_Start)执行一次。获取所有要注册的类型。
1 List<Type> tList = new List<Type>(); 2 var ass = System.Reflection.Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "\\bin\\UserApi.dll"); 3 4 var uType = ass.GetType("UserApi.User"); 5 var gType = ass.GetType("UserApi.Goods"); 6 var oType = ass.GetType("UserApi.Order"); 7 tList.Add(uType); 8 tList.Add(gType); 9 tList.Add(oType); 10 11 DbHelper.InitDbHelper(tList);
以下是使用语句
//以下操作可能存在于不同的物业模块中 using(DbHelper<User> db = new DbHelper<UserApi.User>()) { db.Set<User>().Add(new UserApi.User { userId = "zxq", age = 18, userName = "zxq", sex="女" }); db.SaveChanges(); } //联三个表 using(DbHelper<User, Order, Goods> db = new DbHelper<UserApi.User, Order, Goods>()) { var u = db.Set<User>(); var o = db.Set<Order>(); var g = db.Set<Goods>(); var q = from uu in u join oo in o on uu.userId equals oo.userId join gg in g on oo.goodsId equals gg.goodsId select new { uu, oo, gg }; int count = q.Count(); } //联两个表 using (DbHelper<User,Order> db = new DbHelper<User, Order >()) { db.Set<User>().Add(new UserApi.User { userId = "fzj", age = 18, sex = "男", userName = "fzj" }); db.Set<Order>().Add(new Order { createTime = DateTime.Now, orderId = Guid.NewGuid().ToString("N"), userId = "fzj" }); db.SaveChanges(); var u = db.Set<User>(); var o = db.Set<Order>(); var q = from uu in u join oo in o on uu.userId equals oo.userId select new { uu, oo }; foreach (var item in q) { Console.WriteLine("age:{0} orderId:{1}",item.uu.age, item.oo.orderId); } }
总结:1.以上代码能够解决所有表映射对象必须集中定义的问题,同时解决使用泛型无法联表的问题。
2.对象(表)的定义使用可以由各业务模块自行控制,只需要按照预先约定好,在注册的时候能够找到该类型即可。