在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行 CRUD 的操作,其中存在相当
多的重复代码。为了减少重复代码的出现,通常都会定义一个共用类,实现相似的操作,下面为大
家介绍一下 Entity Framework 时常用到的通用类。
首先在数据库建立起几个关联
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
:Person、Company、Position,三个实体之间通过导航属性进行相
互引用。
下面为大家分别介绍以泛型实现的 Create、Read、Update、Delete 操作:
1. Create
在 ObjectContext 类之中,早已经为大家预定了一个 Create 的操作 AddObject:
void ObjectContext.AddObject(entitySetName string,object entity)
void ObjectSet
.AddObject(T entity)
1 public int Add(T entity) where T : EntityObject
2 {
3 int changedCount = 0;
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 context.AddObject(typeof(T).Name, entity);
9 changedCount = context.SaveChanges();
10 if (changedCount > 0)
11 context.AcceptAllChanges();
12 }
13 }
14 catch (Exception ex)
15 { ........ }
16 return changedCount;
17 }
从下面的测试可以看到,ObjectContext.AddObject(entitySetName string,object entity)已相当成熟,
它不但可以加入单个实体,也可通过导航属性,一次性加入多个关联实体。
1 static void Main(string[] args)
2 {
3 BaseCommand command = new BaseCommand();
4 //建立关联实体
5 Company company = new Company() { CompanyName = "Sun"
6 ,Address="Beijing",Telephone="010-87654321"};
7 Position position = new Position() { PositionName = "Project Manager"
8 , Salary = 15000.00, Company = company };
9 //通过 Add同时加入实体对象 company与 position
10 int n=command.Add(position);
11
12 Console.ReadKey();
13 }
若要使用批量插入,只要在 AddObject 方法前多加一个重复语言即可,在此就不再多作解释了。
1 public int AddList(List entityList) where T : EntityObject
2 {
3 int changedCount = 0;
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 foreach (T entity in entityList)
9 context.AddObject(typeof(T).Name, entity);
10 changedCount = context.SaveChanges();
11 if (changedCount > 0)
12 context.AcceptAllChanges();
13 }
14 }
15 catch (Exception ex)
16 { ....... }
17 return changedCount;
18 }
2. Delete
同样地,ObjectContext 类当中也存在方法 ObjectContext.DeleteObject(object entity)用于删除实体。
首先通过输入的参数 id 建立起 EntityKey 对象,然后在 ObjectContext 查找此实体,若实体存在则使
用 ObjectContext.DeleteObject(object entity)方法把此实体删除 。
1 public int Delete(int id) where T : EntityObject
2 {
3 int changedCount = 0;
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 //建立 EntityKey对象
9 EntityKey entityKey = new EntityKey(
10 "BasicArchitectureEntities." + typeof(T).Name,
"Id", id);
11 //通过 EntityKey找到实体
12 var objResult = context.GetObjectByKey(entityKey);
13 //若实体存在则删除实体
14 if (objResult != null)
15 context.DeleteObject(objResult);
16 changedCount = context.SaveChanges();
17 if (changedCount > 0)
18 context.AcceptAllChanges();
19 }
20 }
21 catch (Exception ex)
22 { ...... }
23 return changedCount;
24 }
ObjectContext.DeleteObject(object entity)与 ObjectContext.AddObject(entitySetName string,object
entity)相同,可以通过导航属性,一次性删除多个关联实体。但如果数据库中存在下面的数据
Company 表:
Position 表:
此时使用此 int Delete(2) 方法删除 Company 对象,系统将会报错。这是由于导航属性
在默认情况下具有延时加载的特性,在系统使用 ObjectContext.GetObjectByKey(entityKey)方法加载
实体时,它的导航属性不会马上加载到上下文当中。而是在调用该导航属性时,对象才会被加载。
因而系统通过 ObjectContext.GetObjectByKey(2)获取 Company 对象时,对应的 Position 对象并未被加
载到上下文当中,所以当删除 Company 对象时,Position 对象不能被同步删除,因而造成逻辑上的
错误。为解决这一问题,可以利用 RelatedEnd.Load()方法提前加载导航属性。
RelatedEnd 是 EntityCollection 、EntityReference 的父类,它们是特定实体类型的对象集合,
该实体类型表示一对多、多对一、多对多的关系。而 RelatedEnd.Load()方法,可以将一个或多个
相关对象提前加载到相关实体当中。
首先通过 ObjectContext.GetObjectByKey(entityKey)方法找到 Company 对象,然后利用反射属性
PropertyInfo 类获取导航属性 Position,最后使用 RelatedEnd.Load()方法,把导航属性加载到当前
上下文中。此时使用 Delete(2)方法删除 Company 对象时,系统将能正常运行,
并把对应的 Position 对象一并删除。
1 public int Delete(int id)
2 where PKEntity : EntityObject
3 where FKEntity : EntityObject
4 {
5 int changedCount = 0;
6 try
7 {
8 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
9 {
10 //根据软件 Id建立 EntityKey对象
11 EntityKey entityKey = new EntityKey(
12 "BasicArchitectureEntities." +
typeof(PKEntity).Name, "Id", id);
13 //根据 EntityKey查找对应对象
14 PKEntity objResult = context.GetObjectByKey(entityKey) as
PKEntity;
15 //根据 FKEntity加载导航属性
16 PropertyInfo propertyInfo = typeof(PKEntity).GetProperty(
17 typeof(FKEntity).Name);
18 EntityCollection FKEntityList =
propertyInfo.GetValue(
19 objResult, null) as EntityCollection;
20
21 if (FKEntityList != null)
22 FKEntityList.Load();
23
24 if (objResult != null)
25 context.DeleteObject(objResult);
26 changedCount = context.SaveChanges();
27
28 if (changedCount > 0)
29 context.AcceptAllChanges();
30 }
31 }
32 catch (Exception ex)
33 { ........ }
34 return changedCount;
35 }
通过下面的方法也可根据输入的委托 predicate,批量删除有关的数据。
1 public int Delete(Func predicate) where T: EntityObject
2 {
3 int changedCount = 0;
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 //根据输入的委托查找数据
9 var list = context.CreateObjectSet().Where(predicate);
10 //若存在数据,删除有关数据
11 if (list.Count() > 0)
12 foreach (var obj in list)
13 context.DeleteObject(obj);
14
15 changedCount = context.SaveChanges();
16 if (changedCount > 0)
17 context.AcceptAllChanges();
18 }
19 }
20 catch (Exception ex)
21 { ...... }
22 return changedCount;
23 }
与前面的例子相同,当使用 Delete(x=>x.Id==2) 方法删除 Company 对象时,由于导航属
性 Position 处于延迟加载的状态,以致系统无法实现同步删除,从而令数据出现逻辑性的错误。
此时使用类似的方法,利用 RelatedEnd.Load() 把导航属性提前加入到上下文中,再删除
Company 对象时,系统就可以把对应 Position 对象一并删除。
1 public int Delete(Func predicate)
2 where PKEntity : EntityObject
3 where FKEntity : EntityObject
4 {
5 int changedCount = 0;
6 try
7 {
8 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
9 {
10 //根据输入的委托查找数据
11 var list =
context.CreateObjectSet().Where(predicate);
12 //若数目大于 0,删除有关数据
13 if (list.Count() > 0)
14 {
15 foreach (var obj in list)
16 {
17 //在删除前加载其导航属性
18 PropertyInfo propertyInfo = typeof(PKEntity)
19 .GetProperty(typeof(FKEntity).Name);
20 EntityCollection FKEntityList =
propertyInfo
21 .GetValue(obj, null) as
EntityCollection;
22 if (FKEntityList.Count > 0)
23 FKEntityList.Load();
24
25 context.DeleteObject(obj);
26 }
27 }
28 changedCount = context.SaveChanges();
29
30 if (changedCount > 0)
31 context.AcceptAllChanges();
32 }
33 }
34 catch (Exception ex)
35 { ....... }
36 return changedCount;
37 }
此时使用 Delete(x=>x.Id==2),这样就可以把 Company 对象和相关的 Position 对
象同时删除。
3. Update
ObjectContext 中存在方法 ObjectContext.ApplyCurrentValues 和
ObjectContext.ApplyOriginalValues,用于把将标量值从实体复制到 ObjectContext 中具有相
同主键的对象集中。
注意:在调用此方法前必须把实体预先加载到当前上下文当中,要不然系统将会显示
“objectstatemanager 无法跟踪具有相同键的多个对象” 的错误。
由于 DAL 层的对象大部分使用单体模式进行开发,而 BaseCommand 是一个共用对象,在共同
操作时,Create、Delete、Read 等操作一般不会对实体造成逻辑性的影响。但如果有多个实体
同时调用 Update 操作,就有可能对实体造成逻辑性影响。为了避免这一事件的发生,此处使
用方法锁定的模式,以 lock(object) 锁定某一对象,以确保在同一时间内只会对一个实体进行
更新。
首先通过反射方式获取对象的 Id,然后通过 ObjectContext.GetObjectByKey(entityKey) 方法把
实体加载到当前上下文当中,最后利用 ObjectContext.ApplyCurrentValues 方法,把新
加入的实体的属性复制当前上下文。
1 public class BaseCommand
2 {
3 private object o = new object();
4
5 public int Update(T entity) where T : EntityObject
6 {
7 lock (o)
8 {
9 int changedCount = 0;
10 Type type = typeof(T);
11
12 try
13 {
14 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
15 {
16 //获取实体的 Id属性
17 PropertyInfo property = type.GetProperty("Id");
18 object id = property.GetValue(entity, null);
19 //根据 Id获取上下文中的对应实体
20 EntityKey entityKey = new
EntityKey("BasicArchitectureEntities."
21 + type.Name, "Id", id);
22 var objResult = context.GetObjectByKey(entityKey);
23 //更新实体属性
24 if (objResult != null)
25 context.ApplyCurrentValues(type.Name, entity);
26
27 changedCount = context.SaveChanges();
28 if (changedCount > 0)
29 context.AcceptAllChanges();
30 }
31 }
32 catch (Exception ex)
33 { ... }
34 return changedCount;
35 }
36 }
37 }
在一对多,多对一关系时,也可以使用以下方法进行导航属性的同步更新。首先通过反射获取主实
体的主键 Id,然后建立 EntityKey 对象,再通过 ObjectContext.GetObjectByKey(entityKey)方法在当前
上下文当中获取此实体,最后通过 ObjectContext.ApplyCurrentValues 方法,把新加入的实
体的属性复制当前上下文。
下一步就是对导航属性进行更新,首先通过反射获取外键属性,然后对一对多,多对一的关系进行
分别处理。在一对多关系时,把导航属性转换成 EntityCollection对象集合,然后通过
ObjectContext.ApplyCurrentValues 方法对集合中的每个对象进行逐个更新。
在多对一关系时,直接把导航属性转换成 T2 类型的对象进行更新。
1 public int Update(T1 entity)
2 where T1 : EntityObject
3 where T2 : EntityObject
4 {
5 lock (o)
6 {
7 int changedCount = 0;
8 Type typeT1 = typeof(T1);
9 Type typeT2 = typeof(T2);
10 try
11 {
12 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
13 {
14 PropertyInfo property = typeT1.GetProperty("Id");
15 object id = property.GetValue(entity, null);
16
17 //根据软件 Id建立 EntityKey对象
18 EntityKey entityKey = new
EntityKey("BasicArchitectureEntities."
19 + typeT1.Name, "Id", id);
20 //根据 EntityKey查找对应对象
21 T1 objT1 = context.GetObjectByKey(entityKey) as T1;
22 //在上下文中更新当前对象
23 if (objT1 != null)
24 context.ApplyCurrentValues(typeT1.Name,
entity);
25
26 //获取外键属性
27 PropertyInfo propertyInfo =
typeT1.GetProperty(typeT2.Name);
28
29 //在一对多关键时更新导航属性
30 var T2List = propertyInfo.GetValue(entity, null)
31 as EntityCollection;
32 if (T2List != null)
33 {
34 foreach (var obj in T2List.ToList())
35 {
36 var oldEntity =
context.GetObjectByKey(obj.EntityKey);
37 if (oldEntity != null)
38
context.ApplyCurrentValues(typeT2.Name, obj);
39 }
40 }
41
42 //在多对一,一对一关系时更新导航属性
43 var objT2 = propertyInfo.GetValue(entity, null) as T2;
44 if (objT2!= null)
45 {
46 var oldEntity =
context.GetObjectByKey(objT2.EntityKey);
47 if (oldEntity != null)
48 context.ApplyCurrentValues(typeT2.Name,
objT2);
49 }
50
51 changedCount = context.SaveChanges();
52 if (changedCount > 0)
53 context.AcceptAllChanges();
54 }
55 catch (Exception ex)
56 { ...... }
57 return changedCount;
58 }
59 }
通过此方法,无论你要通过 Company 同步更新 Position,还是反过来通过 Position 同步更新
Company,系统也能正常运行。
4. Read
Read 是 CRUD 中最常见的,下面就为大家介绍最通用的几种方法
4.1 通过 Id 获取单个实体
1 public T GetObject(int id) where T : EntityObject
2 {
3 try
4 {
5 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
6 {
7 EntityKey entityKey = new EntityKey("BasicArchitectureEntities."
8 + typeof(T).Name, "Id", id);
9 var objResult = context.GetObjectByKey(entityKey);
10 return objResult as T;
11 }
12 }
13 catch (Exception ex)
14 {
15 return null;
16 }
17 }
4.2 通过输入的 Func委托获取对象
1 public T GetObject(Func predicate) where T : EntityObject
2 {
3 try
4 {
5 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
6 {
7 var objectSet = context.CreateObjectSet().Where(predicate);
8 if (objectSet.Count() > 0)
9 return objectSet.First();
10 else
11 return null;
12 }
13 }
14 catch (Exception ex)
15 {
16 return null;
17 }
18 }
4.3 通过输入的 Func委托获取对象,并同时加载单个导航属性
1 public T GetObject(Func predicate,string includePath)
2 where T : EntityObject
3 {
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 var objectQuery = context.CreateObjectSet()
9 .Include(includePath)
10 .Where(predicate);
11
12 if (objectQuery.Count() > 0)
13 return objectQuery.First();
14 else
15 return null;
16 }
17 }
18 catch (Exception ex)
19 {
20 return null;
21 }
22 }
4.4 通过输入的 Func委托获取对象,并同时加载多个导航属性
1 public T GetObject(Func predicate, string[] includePath)
2 where T : EntityObject
3 {
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 var list = context.CreateObjectSet().Where("1==1");
9
10 foreach (var path in includePath)
11 list=list.Include(path);
12
13 var returnValue = list.Where(predicate).ToList();
14
15 if (returnValue.Count() > 0)
16 return returnValue.First();
17 else
18 return null;
19 }
20 }
21 catch (Exception ex)
22 {
23 return null;
24 }
25 }
4.5 通过输入的 Func委托获取对象集合
1 public IList GetList(Func func) where T:EntityObject
2 {
3 try
4 {
5 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
6 {
7 ObjectSet objectSet = context.CreateObjectSet();
8 IList list = objectSet.Where(func).ToList();
9 return list;
10 }
11 }
12 catch (Exception ex)
13 {
14 return null;
15 }
16 }
4.6 通过输入的 Func委托获取对象集合,并同时加入单个导航属性
1 public IList GetList(Func func,string includePath)
2 where T : EntityObject
3 {
4 try
5 {
6 using (BasicArchitectureEntities context = new
BasicArchitectureEntities())
7 {
8 ObjectSet objectSet = context.CreateObjectSet();
9 IList list =
objectSet.Include(includePath).Where(func).ToList();
10 return list;
11 }
12 }
13 catch (Exception ex)
14 {
15 return null;
16 }
17 }
4.7 通过输入的 Func委托获取对象集合,并同时加入多个导航属性
1 public IList GetList(Func func, string[