如何搭建.NET Entity Framework分布式应用系统框架
展开全部
一、 前言
ado.net entity framework(以下简称ef)是微软推出的一套o/rm框架,如果用过linq to sql的人会比较容易理解,因为linq to sql是微软在.net framework 4.0时推出的一套轻量级的o/rm框架,但是只支持sql server一种数据库。至.net framework 3.5 sp1时,才推出entity framework,可以通过实现不同的provider来支持不同的数据库(当然微软还是只内置sql server的provider,其它数据库的provider么,需要第三方开发)。ef加上linq,这是.net开发上的一个巨大进步,.net程序员以对象方式操作数据,以类sql语法在程序里查询数据,大大减少了繁琐的构造sql语句的工作,可以更加专注于编写业务逻辑代码。但是在多层架构的分布式应用系统中,实体对象通过远程序列化到客户端时,这些实体会与其数据上下文(也就是实体容器)分离,在客户端无法对实体直接进行查询以及cud(create,update,delete)操作,下面以sql server为数据库,remoting+entity framework3.5作为数据服务层,winform作为客户端,讲述一下如何使用ef框架搭建多层分布式应用系统。
二、 技术分析
1. 通过远程客户端传输过来的实体,都是处于分离状态(entitystate属性值为detached),所以在多层应用程序中的服务端实现实体的更新或删除时,关键是如何把实体附加回实体容器中。msdn上关于对分离实体的查询和cud操作描述如下:
1) 附加对象(实体框架)
在实体框架的某个对象上下文内执行查询时,返回的对象会自动附加到该对象上下文。还可以将从源而不是从查询获得的对象附加到对象上下文。您可以附加以前分离的对象、由 notracking 查询返回的对象或从对象上下文的外部获取的对象。还可以附加存储在 asp.net 应用程序的视图
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
状态中的对象或从远程方法调用或 web 服务返回的对象。
使用下列方法之一将对象附加到对象上下文:
· 调用 objectcontext 上的 addobject 将对象附加到对象上下文。当对象为数据源中尚不存在的新对象时采用此方法。
· 调用 objectcontext上的 attach 将对象附加到对象上下文。当对象已存在于数据源中但当前尚未附加到上下文时采用此方法。有关更多信息,请参见如何:附加相关对象(实体框架)。
· 调用 objectcontext的 attachto,以将对象附加到对象上下文中的特定实体集。如果对象具有 null(在 visual basic 中为 nothing)entitykey 值,也可以执行此操作。
· 调用 objectcontext上的 applypropertychanges。当对象已存在于数据源中,并且分离的对象具有您希望保存的属性更新时采用此方法。如果简单地附加该对象,则属性更改将丢失。有关更多信息,请参见如何:应用对已分离对象的更改(实体框架)。
2) 应用对已分离对
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
象的更改(实体框架)示例代码
view code
2. 实现动态条件查询。在本地环境中,对于linq,我们可以通过动态构造lambda表达式树来实现动态条件查询,但是在远程环境中,lamdba表达式不支持远程序列化传输,只能通过objectcontext的createquery方法实现,但幸好微软后来又提供了一个linq动态查询扩展库dynamic.cs,使用起来更方便,于是采用它实现。
3. ef中核心抽象类是objectcontext,实体容器都从它派生,实体容器上的cud方法其实都是通过调用objectcontext的cud操作方法实现的。
1) addobject(string,object):表示添加实体object到实体容器,只要实体的entitykey值为空,无论是否detached状态均可以通过此方法实现添加操作。
2) applypropertychanges(string,object)表示把分离状态的实体object上的所作的修改更新回容器中已存在的对应的实体,执行条件有两个:①实体处于分离状态,②实体容器中存在主键值与其相同的且为unchanged状态的实体,所以,当我们需要更新一个detached状态的实体时,可以先把一个具有原始值的相同键值的实体附加回容器中,或者直接执行一下查询,从数据库中取出该实体。
3) deleteobject(object)表示从实体容器中删除一个实体,执行条件是该实体存在于实体容器中,所以删除一个detach状态的实体之前,需要把它通过attach方法附加回实体容器中。
4. 实体对象也是基于抽象类entityobject派生的,由此我们完全可以用contextobject和entityobject实现服务端对实体的查询和cud方法,其实现子类在运行时由客户端注入,从而使服务端和数据库实现松耦合。
5. 下图是msdn上关于在数据访问层中使用 linq to sql 的 n 层应用程序的基本体系结构图,其实ef的结构也是一样的,不过是把datacontext换成objectcontext。
三、 动手开发
1. 利用ef建立数据库概念模型
新建一个解决方案efservicesystem,添加一个新项目,命名为efmodel,添加项目,在项目下添加一个ado.net entity data model项,命名为efmodel.edmx,选择从数据库生成(假设我们已经建好了一个sql server数据库),一路点击下一步,直至完成。编译项目成功后就算完成。为什么要把数据库模型单独编译成一个dll呢,我将在后面给予解释。
2. 建立数据服务层
在解决方案下再添加一个类库项目,命名为efservice。
1) 利用外观模式,我们把客户端常用的查询和cud操作方法简化为3个方法query<t>,save(t t),delete(t t),根据针对接口编程的设计原则,定义一个cud方法接口供客户端调用。
view code
2) 实现类entityhelper的代码。主要思路是通过构造函数注入数据上下文实例名称,在配置文件取出其程序集限定名,
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
通过反射创建实例,调用实例的相应方法实现接口。
view code
3) 最后,我们创建一个服务工厂类,暴露给客户端,负责以接口方式向客户端提供远程服务对象,数据服务层创建完毕。
view code
4) 补充一下dynamic.cs的内容,省得你去网上找了
view code
3. 创建运行服务的宿主程序。实际开发中,通常选择创建一个windows服务程序来运行remoting,但是服务需要安装才能启动,运行和调试起来都比较繁琐,所以这里创建一个简单的控制台程序来运行它。在解决方案下添加一个控制台程序项目,在program.cs编写如下代码:
view code
配置文件app.config主要包括数据库连接信息以及自己定义一个数据上下文名称(这里和数据库连接名称相同,事实上不必相同),数据库连接信息可以从efmodel项目中配置文件中直接拷贝过来。内容如下:
view code
编译成功后,拷贝efmodel和和efservice两个项目生成的dll文件至可执行文件efservicehost.exe同一目录下,点击运行efservicehost.exe。
4. 最后,我们建立一个winform客户端作为测试。在program.cs注册远程服务:
view code
添加一个窗体form1,放入一个按钮,在按钮点击处理事件里加入下面的代码,示范客户端如何调用远程服务类实现查询和cud操作,简单起见,我不演示数据库的数据变化了,反正,看代码,你懂的。
view code
四、 部署应用
1. 至此,整个系统搭建完毕。在本例中,我把所有项目都统一建立在一个解决方案下,其实是为了演示方便,实际开发时候,完全可以各自独立创建。下面我们来分析一下各个项目的职能和相互之间的引用关系。
1) efmodel:由visual studio 的数据模型工具生成的数据库实例模型,提供数据的查询以及cud操作。不需引用其它项目。
2) efservice:使用数据库实例模型以及实体的抽象基类编写完成,代码里不涉及具体数据库模型实例,运行时通过客户端注入参数和读取配置文件动态生成数据库模型实例,并调用实例的查询和cud方法实现客户端的请求。不需引用其它项目。
3) efservicehost:负责运行remoting服务,如果通过配置文件方式发布服务的话,编译时也不需引用其它项目,我这里引用了efservice项目,是因为使用了代码方式暴露efsservice的服务类。运行时需要将efservice和efmodel的dll文件拷贝至运行目录下。
4) efclient:需要引用efmodel和efservice。(注:因为本例中式使用了remoting作为远程服务,如果是webservice或者wcf则只需添加服务引用,然后在本地生成客户端代理类)。事实上efservice中的实现类entityhelper也可以独立出去,不必让客户端引用,对于客户端而言,仅仅是使用servicefactory和接口ientityhelper就足够了。这样只要接口不变,entityhelper更新的时候,客户端无须更新引用,而且服务端代码可以完全被隔离开客户端,对一些服务端和客户端之间的保密性比较敏感的项目尤为有利。
2. 通过分析我们发现,在开发下一个新项目的时候,即使整个数据库都变了,从sql server变成oracle,数据库服务名变了,表也变了,我们仍然无需修改服务端代码,只需针对新的数据库,生成新的efmodel,然后拷贝dll文件至efservicehost的运行目录下(这也就是我为什么要把efmodel独立成一个项目的原因),再修改一下efservicehost的配置文件中的数据库连接和实体容器名称即可完成新系统的部署。对于客户端来说,也就是更新一下efmodel.dll,还是调用服务端提供的那几个api,便可完成查询和cud操作,不用关心底层的数据库是sql server还是oracle,更不用自己实现对新库新表的查询和cud操作(本来也不用)。当然,对于正在运行的系统,我们也可以针对新建数据库生成新的实体模型dll,拷贝至efservicehost运行目录下,实现热插拔方式扩展数据库,而对原来的系统毫无影响,即使新加的库是不同类型的库。
五、 系统架构图示
ado.net entity framework(以下简称ef)是微软推出的一套o/rm框架,如果用过linq to sql的人会比较容易理解,因为linq to sql是微软在.net framework 4.0时推出的一套轻量级的o/rm框架,但是只支持sql server一种数据库。至.net framework 3.5 sp1时,才推出entity framework,可以通过实现不同的provider来支持不同的数据库(当然微软还是只内置sql server的provider,其它数据库的provider么,需要第三方开发)。ef加上linq,这是.net开发上的一个巨大进步,.net程序员以对象方式操作数据,以类sql语法在程序里查询数据,大大减少了繁琐的构造sql语句的工作,可以更加专注于编写业务逻辑代码。但是在多层架构的分布式应用系统中,实体对象通过远程序列化到客户端时,这些实体会与其数据上下文(也就是实体容器)分离,在客户端无法对实体直接进行查询以及cud(create,update,delete)操作,下面以sql server为数据库,remoting+entity framework3.5作为数据服务层,winform作为客户端,讲述一下如何使用ef框架搭建多层分布式应用系统。
二、 技术分析
1. 通过远程客户端传输过来的实体,都是处于分离状态(entitystate属性值为detached),所以在多层应用程序中的服务端实现实体的更新或删除时,关键是如何把实体附加回实体容器中。msdn上关于对分离实体的查询和cud操作描述如下:
1) 附加对象(实体框架)
在实体框架的某个对象上下文内执行查询时,返回的对象会自动附加到该对象上下文。还可以将从源而不是从查询获得的对象附加到对象上下文。您可以附加以前分离的对象、由 notracking 查询返回的对象或从对象上下文的外部获取的对象。还可以附加存储在 asp.net 应用程序的视图
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
状态中的对象或从远程方法调用或 web 服务返回的对象。
使用下列方法之一将对象附加到对象上下文:
· 调用 objectcontext 上的 addobject 将对象附加到对象上下文。当对象为数据源中尚不存在的新对象时采用此方法。
· 调用 objectcontext上的 attach 将对象附加到对象上下文。当对象已存在于数据源中但当前尚未附加到上下文时采用此方法。有关更多信息,请参见如何:附加相关对象(实体框架)。
· 调用 objectcontext的 attachto,以将对象附加到对象上下文中的特定实体集。如果对象具有 null(在 visual basic 中为 nothing)entitykey 值,也可以执行此操作。
· 调用 objectcontext上的 applypropertychanges。当对象已存在于数据源中,并且分离的对象具有您希望保存的属性更新时采用此方法。如果简单地附加该对象,则属性更改将丢失。有关更多信息,请参见如何:应用对已分离对象的更改(实体框架)。
2) 应用对已分离对
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
象的更改(实体框架)示例代码
view code
2. 实现动态条件查询。在本地环境中,对于linq,我们可以通过动态构造lambda表达式树来实现动态条件查询,但是在远程环境中,lamdba表达式不支持远程序列化传输,只能通过objectcontext的createquery方法实现,但幸好微软后来又提供了一个linq动态查询扩展库dynamic.cs,使用起来更方便,于是采用它实现。
3. ef中核心抽象类是objectcontext,实体容器都从它派生,实体容器上的cud方法其实都是通过调用objectcontext的cud操作方法实现的。
1) addobject(string,object):表示添加实体object到实体容器,只要实体的entitykey值为空,无论是否detached状态均可以通过此方法实现添加操作。
2) applypropertychanges(string,object)表示把分离状态的实体object上的所作的修改更新回容器中已存在的对应的实体,执行条件有两个:①实体处于分离状态,②实体容器中存在主键值与其相同的且为unchanged状态的实体,所以,当我们需要更新一个detached状态的实体时,可以先把一个具有原始值的相同键值的实体附加回容器中,或者直接执行一下查询,从数据库中取出该实体。
3) deleteobject(object)表示从实体容器中删除一个实体,执行条件是该实体存在于实体容器中,所以删除一个detach状态的实体之前,需要把它通过attach方法附加回实体容器中。
4. 实体对象也是基于抽象类entityobject派生的,由此我们完全可以用contextobject和entityobject实现服务端对实体的查询和cud方法,其实现子类在运行时由客户端注入,从而使服务端和数据库实现松耦合。
5. 下图是msdn上关于在数据访问层中使用 linq to sql 的 n 层应用程序的基本体系结构图,其实ef的结构也是一样的,不过是把datacontext换成objectcontext。
三、 动手开发
1. 利用ef建立数据库概念模型
新建一个解决方案efservicesystem,添加一个新项目,命名为efmodel,添加项目,在项目下添加一个ado.net entity data model项,命名为efmodel.edmx,选择从数据库生成(假设我们已经建好了一个sql server数据库),一路点击下一步,直至完成。编译项目成功后就算完成。为什么要把数据库模型单独编译成一个dll呢,我将在后面给予解释。
2. 建立数据服务层
在解决方案下再添加一个类库项目,命名为efservice。
1) 利用外观模式,我们把客户端常用的查询和cud操作方法简化为3个方法query<t>,save(t t),delete(t t),根据针对接口编程的设计原则,定义一个cud方法接口供客户端调用。
view code
2) 实现类entityhelper的代码。主要思路是通过构造函数注入数据上下文实例名称,在配置文件取出其程序集限定名,
此文来自: 马开东博客 转载请注明出处 网址: http://www.makaidong.com
通过反射创建实例,调用实例的相应方法实现接口。
view code
3) 最后,我们创建一个服务工厂类,暴露给客户端,负责以接口方式向客户端提供远程服务对象,数据服务层创建完毕。
view code
4) 补充一下dynamic.cs的内容,省得你去网上找了
view code
3. 创建运行服务的宿主程序。实际开发中,通常选择创建一个windows服务程序来运行remoting,但是服务需要安装才能启动,运行和调试起来都比较繁琐,所以这里创建一个简单的控制台程序来运行它。在解决方案下添加一个控制台程序项目,在program.cs编写如下代码:
view code
配置文件app.config主要包括数据库连接信息以及自己定义一个数据上下文名称(这里和数据库连接名称相同,事实上不必相同),数据库连接信息可以从efmodel项目中配置文件中直接拷贝过来。内容如下:
view code
编译成功后,拷贝efmodel和和efservice两个项目生成的dll文件至可执行文件efservicehost.exe同一目录下,点击运行efservicehost.exe。
4. 最后,我们建立一个winform客户端作为测试。在program.cs注册远程服务:
view code
添加一个窗体form1,放入一个按钮,在按钮点击处理事件里加入下面的代码,示范客户端如何调用远程服务类实现查询和cud操作,简单起见,我不演示数据库的数据变化了,反正,看代码,你懂的。
view code
四、 部署应用
1. 至此,整个系统搭建完毕。在本例中,我把所有项目都统一建立在一个解决方案下,其实是为了演示方便,实际开发时候,完全可以各自独立创建。下面我们来分析一下各个项目的职能和相互之间的引用关系。
1) efmodel:由visual studio 的数据模型工具生成的数据库实例模型,提供数据的查询以及cud操作。不需引用其它项目。
2) efservice:使用数据库实例模型以及实体的抽象基类编写完成,代码里不涉及具体数据库模型实例,运行时通过客户端注入参数和读取配置文件动态生成数据库模型实例,并调用实例的查询和cud方法实现客户端的请求。不需引用其它项目。
3) efservicehost:负责运行remoting服务,如果通过配置文件方式发布服务的话,编译时也不需引用其它项目,我这里引用了efservice项目,是因为使用了代码方式暴露efsservice的服务类。运行时需要将efservice和efmodel的dll文件拷贝至运行目录下。
4) efclient:需要引用efmodel和efservice。(注:因为本例中式使用了remoting作为远程服务,如果是webservice或者wcf则只需添加服务引用,然后在本地生成客户端代理类)。事实上efservice中的实现类entityhelper也可以独立出去,不必让客户端引用,对于客户端而言,仅仅是使用servicefactory和接口ientityhelper就足够了。这样只要接口不变,entityhelper更新的时候,客户端无须更新引用,而且服务端代码可以完全被隔离开客户端,对一些服务端和客户端之间的保密性比较敏感的项目尤为有利。
2. 通过分析我们发现,在开发下一个新项目的时候,即使整个数据库都变了,从sql server变成oracle,数据库服务名变了,表也变了,我们仍然无需修改服务端代码,只需针对新的数据库,生成新的efmodel,然后拷贝dll文件至efservicehost的运行目录下(这也就是我为什么要把efmodel独立成一个项目的原因),再修改一下efservicehost的配置文件中的数据库连接和实体容器名称即可完成新系统的部署。对于客户端来说,也就是更新一下efmodel.dll,还是调用服务端提供的那几个api,便可完成查询和cud操作,不用关心底层的数据库是sql server还是oracle,更不用自己实现对新库新表的查询和cud操作(本来也不用)。当然,对于正在运行的系统,我们也可以针对新建数据库生成新的实体模型dll,拷贝至efservicehost运行目录下,实现热插拔方式扩展数据库,而对原来的系统毫无影响,即使新加的库是不同类型的库。
五、 系统架构图示
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询