欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET基础篇——Entity Framework数据转换层通用类

程序员文章站 2022-06-25 15:56:11
在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行crud的操作,其中存在相当多的重复代码。为了减少重复代码的出现,通常都会定义一个共用类,实现相似的操作,下面为大家介绍...

在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行crud的操作,其中存在相当多的重复代码。为了减少重复代码的出现,通常都会定义一个共用类,实现相似的操作,下面为大家介绍一下entity framework时常用到的通用类。
首先在建立起几个关联表:person、company、position,三个实体之间通过导航属性进行相互引用。
 .NET基础篇——Entity Framework数据转换层通用类

下面为大家分别介绍以泛型实现的 create、read、update、delete 操作:
1. create
在objectcontext类之中,早已经为大家预定了一个create 的操作 addobject:
void objectcontext.addobject(entitysetname string,object entity)
void objectset<t>.addobject(t entity)
 
 1         public int add<t>(t entity) where t : entityobject
 2         {
 3             int changedcount = 0;
 4             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 5             {
 6                 try
 7                 {
 8                     using (basicarchitectureentities context = new basicarchitectureentities())
 9                     {
10                         context.addobject(typeof(t).name, entity);
11                         changedcount = context.savechanges();
12                         if (changedcount > 0)
13                             context.acceptallchanges();
14                         scope.complete();
15                     }
16                 }
17                 catch (exception ex)
18                 { ........ }
19             }
20             return changedcount;
21         }
 
从下面的测试可以看到,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" ,address="beijing",telephone="010-87654321"};
 6             position position = new position() { positionname = "project manager", salary = 15000.00, company = company };
 7             //通过add<t>同时加入实体对象company与position
 8             int n=command.add<position>(position);
 9
10             console.readkey();
11         }
 
若要使用批量插入,只要在addobject方法前多加一个重复语言即可,在此就不再多作解释了。
 
 1         public int addlist<t>(list<t> entitylist) where t : entityobject
 2         {
 3             int changedcount = 0;
 4             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 5             {
 6                 try
 7                 {
 8                     using (basicarchitectureentities context = new basicarchitectureentities())
 9                     {
10                         foreach (t entity in entitylist)
11                             context.addobject(typeof(t).name, entity);
12                         changedcount = context.savechanges();
13                         if (changedcount > 0)
14                             context.acceptallchanges();
15                         scope.complete();
16                     }
17                 }
18                 catch (exception ex)
19                 { ....... }
20             }
21             return changedcount;
22         }
 
 
2. delete
同样地,objectcontext 类当中也存在方法 objectcontext.deleteobject(object entity)用于删除实体。
首先通过输入的参数 id 建立起entitykey对象,然后在objectcontext查找此实体,若实体存在则使用objectcontext.deleteobject(object entity)方法把此实体删除 。
 
 1         public int delete<t>(int id) where t : entityobject
 2         {
 3             int changedcount = 0;
 4             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 5             {
 6                 try
 7                 {
 8                     using (basicarchitectureentities context = new basicarchitectureentities())
 9                     {
10                         //建立entitykey对象
11                         entitykey entitykey = new entitykey("basicarchitectureentities." + typeof(t).name, "id", id);
12                         //通过entitykey找到实体
13                         var objresult = context.getobjectbykey(entitykey);
14                         //若实体存在则删除实体
15                         if (objresult != null)
16                             context.deleteobject(objresult);
17                         changedcount = context.savechanges();
18                         if (changedcount > 0)
19                             context.acceptallchanges();
20                        
21                         scope.complete();
22                     }
23                 }
24                 catch (exception ex)
25                 { ...... }
26             }
27             return changedcount;
28         }
 
objectcontext.deleteobject(object entity)与objectcontext.addobject(entitysetname string,object entity)相同,可以通过导航属性,一次性删除多个关联实体。但如果数据库中存在下面的数据
company表:
 .NET基础篇——Entity Framework数据转换层通用类

position表:
 .NET基础篇——Entity Framework数据转换层通用类
 
此时使用此 int delete<company>(2) 方法删除company对象,将会报错。这是由于导航属性在默认情况下具有延时加载的特性,在系统使用objectcontext.getobjectbykey(entitykey)方法加载实体时,它的导航属性不会马上加载到上下文当中。而是在调用该导航属性时,对象才会被加载。
因而系统通过objectcontext.getobjectbykey(2)获取company对象时,对应的position对象并未被加载到上下文当中,所以当删除company对象时,position对象不能被同步删除,因而造成逻辑上的错误。为解决这一问题,可以利用relatedend.load()方法提前加载导航属性。
relatedend是entitycollection<tentity> 、entityreference的父类,它们是特定实体类型的对象集合,该实体类型表示一对多、多对一、多对多的关系。而relatedend.load()方法,可以将一个或多个相关对象提前加载到相关实体当中。
首先通过objectcontext.getobjectbykey(entitykey)方法找到company对象,然后利用反射属性propertyinfo类获取导航属性position,最后使用relatedend.load()方法,把导航属性加载到当前上下文中。此时使用delete<company,position>(2)方法删除company对象时,系统将能正常运行,并把对应的position对象一并删除。
 
 1         public int delete<pkentity, fkentity>(int id)
 2             where pkentity : entityobject
 3             where fkentity : entityobject
 4         {
 5             int changedcount = 0;
 6             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 7             {
 8                 try
 9                 {
10                     using (basicarchitectureentities context = new basicarchitectureentities())
11                     {
12                         //根据软件id建立entitykey对象
13                         entitykey entitykey = new entitykey("basicarchitectureentities." + typeof(pkentity).name, "id", id);
14                         //根据entitykey查找对应对象
15                         pkentity objresult = context.getobjectbykey(entitykey) as pkentity;
16                         //根据fkentity加载导航属性
17                         propertyinfo propertyinfo = typeof(pkentity).getproperty(typeof(fkentity).name);
18                         entitycollection<fkentity> fkentitylist = propertyinfo.getvalue(objresult, null)
19                             as entitycollection<fkentity>;
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                         scope.complete();
32                     }
33                 }
34                 catch (exception ex)
35                 { ........ }
36             }
37             return changedcount;
38         }
 
通过下面的方法也可根据输入的委托predicate,批量删除有关的数据。
 
 1         public int delete<t>(func<t,bool> predicate) where t: entityobject
 2         {
 3             int changedcount = 0;
 4             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 5             {
 6                 try
 7                 {
 8                     using (basicarchitectureentities context = new basicarchitectureentities())
 9                     {
10                         //根据输入的委托查找数据
11                         var list = context.createobjectset<t>().where(predicate);
12                         //若存在数据,删除有关数据
13                         if (list.count() > 0)
14                             foreach (var obj in list)
15                                 context.deleteobject(obj);
16
17                         changedcount = context.savechanges();
18                         if (changedcount > 0)
19                             context.acceptallchanges();
20
21                         scope.complete();
22                     }
23                 }
24                 catch (exception ex)
25                 { ...... }
26             }
27             return changedcount;
28         }
 
与前面的例子相同,当使用 delete<company>(x=>x.id==2) 方法删除 company 对象时,由于导航属性 position 处于延迟加载的状态,以致系统无法实现同步删除,从而令数据出现逻辑性的错误。
此时使用类似的方法,利用 relatedend.load() 把导航属性提前加入到上下文中,再删除company对象时,系统就可以把对应 position 对象一并删除。
 
 1         public int delete<pkentity, fkentity>(func<pkentity,bool> predicate)
 2             where pkentity : entityobject
 3             where fkentity : entityobject
 4         {
 5             int changedcount = 0;
 6             using (transactionscope scope = new transactionscope(transactionscopeoption.required))
 7             {
 8                 try
 9                 {
10                     using (basicarchitectureentities context = new basicarchitectureentities())
11                     {
12                         //根据输入的委托查找数据
13                         var list = context.createobjectset<pkentity>().where(predicate);
14                         //若数目大于0,删除有关数据
15                         if (list.count() > 0)
16                         {
17                             foreach (var obj in list)
18                             {
19                                 //在删除前加载其导航属性
20                                 propertyinfo propertyinfo = typeof(pkentity).getproperty(typeof(fkentity).name);
21                                 entitycollection<fkentity> fkentitylist = propertyinfo.getvalue(obj, null)
22                                     as entitycollection<fkentity>;
23                                 if (fkentitylist.count > 0)
24                                     fkentitylist.load();
25
26                                 context.deleteobject(obj);
27                             }
28                         }
29
30                         changedcount = context.savechanges();
31
32                         if (changedcount > 0)
33                             context.acceptallchanges();
34
35                         scope.complete();
36                     }
37                 }
38                 catch (exception ex)
39                 { ....... }
40             }
41             return changedcount;
42         }
 
此时使用delete<company,position>(x=>x.id==2),这样就可以把company对象和相关的position对象同时删除。
 
3. update
objectcontext 中存在方法 objectcontext.applycurrentvalues<tentity> 和 objectcontext.applyoriginalvalues<tentity>,用于把将标量值从实体复制到 objectcontext 中具有相同主键的对象集中。
注意:在调用此方法前必须把实体预先加载到当前上下文当中,要不然系统将会显示  “objectstatemanager 无法跟踪具有相同键的多个对象” 的错误。
由于dal层的对象大部分使用单体模式进行开发,而basecommand是一个共用对象,在共同操作时,create、delete、read 等操作一般不会对实体造成逻辑性的影响。但如果有多个实体同时调用 update 操作,就有可能对实体造成逻辑性影响。为了避免这一事件的发生,此处使用方法锁定的模式,以 lock(object) 锁定某一对象,以确保在同一时间内只会对一个实体进行更新。
首先通过反射方式获取对象的id,然后通过 objectcontext.getobjectbykey(entitykey) 方法把实体加载到当前上下文当中,最后利用 objectcontext.applycurrentvalues<tentity> 方法,把新加入的实体的属性复制当前上下文。
 
 1     public class basecommand
 2     {
 3         private object o = new object();
 4        
 5         public int update<t>(t entity) where t : entityobject
 6         {
 7             lock (o)
 8             {
 9                 int changedcount = 0;
10                 type type = typeof(t);
11
12                 using (transactionscope scope = new transactionscope(transactionscopeoption.required))
13                 {
14                     try
15                     {
16                         using (basicarchitectureentities context = new basicarchitectureentities())
17                         {
18                             //获取实体的id属性
19                             propertyinfo property = type.getproperty("id");
20                             object id = property.getvalue(entity, null);
21                             //根据id获取上下文中的对应实体
22                             entitykey entitykey = new entitykey("basicarchitectureentities."
23                                   + type.name, "id", id);
24                             var objresult = context.getobjectbykey(entitykey);
25                             //更新实体属性
26                             if (objresult != null)
27                                 context.applycurrentvalues<t>(type.name, entity);
28
29                             changedcount = context.savechanges();
30                             if (changedcount > 0)
31                                 context.acceptallchanges();
32
33                             scope.complete();
34                         }
35                     }
36                     catch (exception ex)
37                     { ... }
38                 }
39                 return changedcount;
40             }
41         }
42     }
 
 在一对多,多对一关系时,也可以使用以下方法进行导航属性的同步更新。首先通过反射获取主实体的主键id,然后建立entitykey对象,再通过objectcontext.getobjectbykey(entitykey)方法在当前上下文当中获取此实体,最后通过 objectcontext.applycurrentvalues<tentity> 方法,把新加入的实体的属性复制当前上下文。
下一步就是对导航属性进行更新,首先通过反射获取外键属性,然后对一对多,多对一的关系进行分别处理。在一对多关系时,把导航属性转换成entitycollection<t2>对象集合,然后通过 objectcontext.applycurrentvalues<tentity> 方法对集合中的每个对象进行逐个更新。
在多对一关系时,直接把导航属性转换成t2类型的对象进行更新。
 
 1         public int update<t1, t2>(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
11                 using (transactionscope scope = new transactionscope(transactionscopeoption.required))
12                 {
13                     try
14                     {
15                         using (basicarchitectureentities context = new basicarchitectureentities())
16                         {
17                             propertyinfo property = typet1.getproperty("id");
18                             object id = property.getvalue(entity, null);
19
20                             //根据软件id建立entitykey对象
21                             entitykey entitykey = new entitykey("basicarchitectureentities."
22                                  + typet1.name, "id", id);
23                             //根据entitykey查找对应对象
24                             t1 objt1 = context.getobjectbykey(entitykey) as t1;
25                             //在上下文中更新当前对象
26                             if (objt1 != null)
27                                 context.applycurrentvalues<t1>(typet1.name, entity);
28
29                             //获取外键属性
30                             propertyinfo propertyinfo = typet1.getproperty(typet2.name);
31
32                             //在一对多关键时更新导航属性
33                             var t2list = propertyinfo.getvalue(entity, null)
34                                    as entitycollection<t2>;
35                             if (t2list != null)
36                             {
37                                 foreach (var obj in t2list.tolist())
38                                 {
39                                     var oldentity = context.getobjectbykey(obj.entitykey);
40                                     if (oldentity != null)
41                                         context.applycurrentvalues<t2>(typet2.name, obj);
42                                 }
43                             }
44
45                             //在多对一,一对一关系时更新导航属性
46                             var objt2 = propertyinfo.getvalue(entity, null) as t2;
47                             if (objt2!= null)
48                             {
49                                 var oldentity = context.getobjectbykey(objt2.entitykey);
50                                 if (oldentity != null)
51                                     context.applycurrentvalues<t2>(typet2.name, objt2);
52                             }
53
54                             changedcount = context.savechanges();
55                             if (changedcount > 0)
56                                 context.acceptallchanges();
57
58                             scope.complete();
59                         }
60                     }
61                     catch (exception ex)
62                     { ...... }
63                 }
64                 return changedcount;
65             }
66         }
 
通过此方法,无论你要通过company同步更新position,还是反过来通过position同步更新company,系统也能正常运行。
 
4. read
read 是crud中最常见的,下面就为大家介绍最通用的几种方法
4.1 通过id获取单个实体
 
 1         public t getobject<t>(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<t,bool>委托获取对象
 
 1         public t getobject<t>(func<t,bool> predicate) where t : entityobject
 2         {
 3             try
 4             {
 5                 using (basicarchitectureentities context = new basicarchitectureentities())
 6                 {
 7                     var objectset = context.createobjectset<t>().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<t,bool>委托获取对象,并同时加载单个导航属性
 
 1         public t getobject<t>(func<t, bool> predicate,string includepath)
 2             where t : entityobject
 3         {
 4             try
 5             {
 6                 using (basicarchitectureentities context = new basicarchitectureentities())
 7                 {
 8                     var objectquery = context.createobjectset<t>()
 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<t,bool>委托获取对象,并同时加载多个导航属性
 
 1         public t getobject<t>(func<t, bool> predicate, string[] includepath)
 2              where t : entityobject
 3         {
 4             try
 5             {
 6                 using (basicarchitectureentities context = new basicarchitectureentities())
 7                 {
 8                     var list = context.createobjectset<t>().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<t,bool>委托获取对象集合
 
 1         public ilist<t> getlist<t>(func<t,bool> func) where t:entityobject
 2         {
 3             try
 4             {
 5                 using (basicarchitectureentities context = new basicarchitectureentities())
 6                 {
 7                     objectset<t> objectset = context.createobjectset<t>();
 8                     ilist<t> list = objectset.where(func).tolist();
 9                     return list;
10                 }
11             }
12             catch (exception ex)
13             {
14                 return null;
15             }
16         }
 
 
4.6通过输入的func<t,bool>委托获取对象集合,并同时加入单个导航属性
 
 1         public ilist<t> getlist<t>(func<t, bool> func,string includepath)
 2              where t : entityobject
 3         {
 4             try
 5             {
 6                 using (basicarchitectureentities context = new basicarchitectureentities())
 7                 {
 8                     objectset<t> objectset = context.createobjectset<t>();
 9                     ilist<t> 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<t,bool>委托获取对象集合,并同时加入多个导航属性
 
 1         public ilist<t> getlist<t>(func<t, bool> func, string[] includepath)
 2             where t : entityobject
 3         {
 4             try
 5             {
 6                 using (basicarchitectureentities context = new basicarchitectureentities())
 7                 {
 8                     var list = context.createobjectset<t>().where("1==1");