前言
自从项目上了。净核心平台用上了实体框架工作核心就再没碰过EntityFramework 6.x版本,目前而言EntityFramework 6.x是用的最多,无论是找工作而言还是提升自身技术而言皆自身收益,同时呢,大多数时间除了工作之外,还留有一小部分时间在写EntityFramework 6.x和实体框架工作核心的书籍,所以将EntityFramework 6.x相当于是从零学起,EntityFramework 6.x又添加了许多特性,所以花了一些时间去看并整理了下来,本节相当于是自己一直未碰到过的问题,于是花了一点时间在多个上下文迁移到不同数据库并实现分布式事务上,作为基础入口且同步于书籍,供阅读者学习也是我的点滴积累,文章如有错误,请指正。
模型建立
在开始EntityFramework 6.x内容叙述之前,我们还是老套路,首先准备模型,我们搞一个预约航班的基本模型,一个是航班实体,另外一个为预约实体,请看如下:
///摘要///航班////公开课航班预订摘要{ ///摘要///航班id////summary public int Flightid { get;设置;} ///摘要///航班名称////摘要公共字符串FilghtName { get设置;} ///摘要///航班号////汇总公共字符串数字{ get设置;} ///摘要///出行日期////汇总公共日期时间旅行日期{获取设置;} }///摘要///预订////汇总公开课预约{ ///汇总///预订id////summary public int BookingId { get;设置;} ///摘要///预订人////摘要公共字符串名称{ get设置;} ///摘要///预订日期////汇总公共日期时间登记日期{获取设置;}=DateTime .现在;}公共类trip requisition { public FlighbookFilter { get;设置;}公共预订酒店{获取设置;} }此类用于维护航班和预约的实体,在创建预约航班时使用。在EntityFramework 6.0版本上出现了基于代码配置(基于代码的配置),对于数据库初始化策略和其他等等配置,我们单独建立一个配置类来维护,而无需如我们以往一样放在DbContext上下文派生类构造函数中,这样一来上下文派生类看起来则洁净很多。
公共类hotellightconfiguration : db配置{ public hotelightconfiguration(){ SetDatabaseInitializer(new DropcreateDatabaSeifmodelChangeshoteldbcontext());SetDatabaseInitializer(新DropCreateDatabaseifModelChangesFlightdbcontext());} }接下来我们再来配置两个DbContext上下文派生类即HotelDbContext和FlightDbContext,并且基本配置信息利用特性来修饰,如下:
[DbConfigurationType(类型为(hotel light configuration))]公共类航班DBcontext : db context { public flight DBcontext()): base(' name=flight connection '){ }公共类dbsetflight预订航班预订{ get设置;}受保护的覆盖void on model creating(DBMolderBuilder模型生成器){模型生成器.配置。添加(新的FlighboongMap());基地. OnModelCreating(模型构建器);} }[DbConfigurationType(类型为(hotel light configuration))]public class Hoteldbcontext : db context { public Hoteldbcontext(): base(' name=reservationconaction '){ } public dbsetreservationreservations { get;设置;}受保护的覆盖void on model creating(DBMolderBuilder模型生成器){模型生成器.配置。添加(新的预留映射());基地. OnModelCreating(模型构建器);} }对应的映射配置已经叙述很多次了,我们不用废话,直接给出。
public class FlighbookingMap : EntityTypeconfigurationFligbooking { public FlighbookingMap(){//table Totable(' Flighboocks ');//key HasKey(k=k . FlighId);//Property Property(p=p . FilghtName)。HasMaxLength(50);属性(p=p . Number);属性(p=p . TravelLingDate);} } public class ReservationMap : EntityTypeConfigurationReservation { public ReservationMap(){//table to table(' Reservations ');//key HasKey(k=k . BookingId);//Property Property(p=p . BookingId)。HasDatabaseGeneratedOption(数据库生成选项。无);属性(p=p .名称)。HasMaxLength(20);属性(p=p . booking date);}}我们将在上面两个上下文中迁移到不同的数据库,所以当然有两个连接字符串。
连接字符串添加名称=“保留连接”连接字符串=“数据源=王鹏;初始目录=保留数据库;集成安全性=真“providerName=”系统。数据。SqlClient/add name=' flight connection ' connectionString='数据源=王鹏;初始目录=飞行数据库;集成安全性=true“provider name=”系统。好了,所有的模型和上下文都已经构建好了,接下来我们要进行迁移。请往下看。
多上下文迁移
迁移上下文没什么好说的。在大多数场景中,应用程序中似乎只有一个上下文,因为幕后只有一个数据库,所以每个人都可以轻松地获得它。当多个上下文迁移对应不同的数据库迁移时,我们如何操作?如果您非常熟悉迁移命令,请将其作为一个回顾。否则,可以作为基本参考。有点啰嗦。让我们输入正文。将模型迁移到数据库并保持它只需要以下三个步骤。
多个上下文被迁移到不同的文件夹目录
启用-迁移命令
添加-迁移命令
更新数据库命令
当统一应用程序中只有一个上下文时,我们只需要Enabel-Migrations。但是,如果有多个上下文,如果没有明确指定上下文,迁移显然会报告错误。首先,我们将项目更改为上下文位于NuGet控制台中的项目。
接下来,运行Enable-Migrations来初始化迁移目录,显然会有迁移异常。
由于有多个上下文,我们需要指定要迁移哪个上下文。在其命令后添加-ContextTypeName来指定上下文,并继续使用-mightorysdirectory来指定迁移目录。最后是下面这个命令(不知道有哪些命令,在每个命令后面加一个[-]条,按Tab键显示想要的命令)。
启用-迁移-上下文类型名称飞行上下文-迁移目录:飞行迁移
接下来,使用Add-Migration命令为挂起的模型更改设置基础框架,也就是说,我们在上一次迁移后更改了模型,并为下一次迁移设置了基础框架。此时,生成的模型状态被挂起或调用以待确定。我们需要在上面生成的FlightMigrations目录下迁移配置类,所以此时,在Add-Migration命令后指定-ConfigurationTypeName,然后通过-Name指定第一个基座名。
添加-迁移-配置类型名实体框架工作事务范围。数据。飞行迁移。配置-名称初始
或者
添加-迁移-配置类型名实体框架工作事务范围。数据。飞行迁移。配置'初始'
最后,只需要更新数据库就可以持久化到数据库中以生成表。
更新-数据库-配置类型名实体框架工作事务范围。数据。飞行迁移。配置
同样,我们使用上面的三步命令来迁移HotelDbContext。最后,我们可以清楚地看到,每个上下文都迁移到不同的目录中,如下所示:
上面的迁移没有错,每个上下文单独迁移生成一个文件夹。我们有没有想过将多个上下文迁移到同一个目录文件夹中并加以区分?当我们只有一个上下文时,默认情况下为我们创建的文件夹是“迁移”,我们将在“迁移”文件夹下生成不同的上下文迁移配置。
多个上下文迁移到同一个文件夹目录
其实这也很简单。我们可以在-migration directionony之后直接指定一个文件夹生成上下文,比如C:ADbContext,这也是由EntityFramework完成的。下面我们来看看。
启用-迁移-上下文类型名称飞行数据库上下文-迁移目录迁移飞行数据库上下文
启用-迁移-上下文类型名称HotelDbContext-迁移目录迁移HotelDbContext
其他两个步骤的运行方式与迁移不同,我们最终会看到期望的结果。
将通过上述迁移生成两个数据库,即飞行数据库和预订数据库,这两个数据库对应于飞行预订和预订表。好了,这是迁移多个上下文的两种方法的结尾。让我们继续本节的主题。
分布式事务
有时我们需要跨数据库管理事务范围。例如,有一个场景,其中有两个数据库db1和db2,而tb1在db1中,tb2在db2中。同时,tb1和tb2是相关的。在上面我们创建的航班和预订模型中,我们需要将航班数据和预订数据同时插入到不同的数据库中,此时需要事务一致性,所以为了处理这样的需求,在。NET 2.0,在系统中。事务命名空间,该类为代码块提供了一种简单的方法来参与事务,而无需与事务本身进行交互。强烈建议在使用块中创建TransactionScope对象。
当TransactionScope被实例化时,事务管理器需要确定参与哪个事务。一旦确认,实例将始终参与事务。创建TransactionScope对象时,我们需要传递一个TransactionScopeOption枚举,该枚举具有以下值:
必需:实例必须需要事务。如果交易已经存在,将使用现有交易;否则,将创建新的事务。要求新:始终为实例创建新事务。抑制:创建实例时,其他现有事务将被抑制,因为实例中的所有操作都是在没有其他现有事务的情况下完成的。接下来,我们使用上述枚举中的第二种方式来实现航班预订。简单的逻辑如下:
公共类MakeReservation { FlightDBContext航班;HotelDBContext酒店;public make reserve(){ flight=new flight db context();hotel=new HotelDBContext();}//事务处理方法public bool reserve trip(trip reserve trip){ bool reserved=false;//使用(varscope=new transaction scope(transaction scope option)绑定事务范围。requires new)){ try {//航班信息航班。航班预订。添加(行程。filght);航班。saveChanges();//预订信息hotel . reservations . add(trip . hotel);酒店。saveChanges();保留=真;//完成交易并提交范围。完成();} catch(Exception ex){ throw ex;} }返回保留;}}上述ReservTrip方法接受TripReservation对象。该方法定义了TransactionScope,在事务的上下文中捆绑了航班和酒店的Create操作,并将代码写入try-catch块。如果两个实体的SaveChanges方法成功执行,事务将完成,否则,事务将回滚。接下来,调用控制器。
公共类trip Controller : Controller { make reserve reserve;public TripController(){ reserve=new make reservation();} public action result Index(){ return View();} public action result Create(){ return View(新的TripReservation());}[httpset]public action result Create(TripRequisition trip info){ try { trip info。Filght . TravellingDate=DateTime。现在;tripinfo。酒店。预订日期=日期时间。现在;var res=reserv。reserve trip(trip info);if(!res) {返回视图('错误');} }捕获(异常){返回视图('错误');}返回视图(“成功”);}}我们添加了航班预订视图:
@ model EntityFrameworkTransactionScope。data . entity . tripreservation @ { viewpag。标题=“创建”;} H2 class=' text-center ' travel/H2 @使用(Html。begin inform()){ table class=' table table-conditioned table-stripped table-bordered ' tr TD table-conditioned table ' tr TD colspan=2 ' class=' text-center '航班信息/TD/tr TD TD航班id :/TD TD TD @ html . editor for(m=m . filght . flight id)/TD/tr TD tr TD航班名称3360/Tdtd @ html . editor for(m=m . filght . filghtname)/TD/tr TD航班号tdtd @ html . editor for(m=m . hotel . booking id)/TD/Tr TD customer name/tdtd @ html . editor for(m=m . hotel . name)/TD/Tr/table/TD/Tr TD col span=' 2 ' class=' text-center '输入类型=' submit ' value=' submit appointment '/TD/Tr/table }视图中显示的UI如下:
为了运行应用程序和检查事务,我们需要使用分布式事务协调器(DTC)服务。该服务协调更新两个或多个事务的受保护资源的事务,如数据库、消息队列、文件系统等。首先,我们需要确保在服务中打开、检查并启用DTC。
接下来,打开DTC设置,请按照以下步骤或直接运行[dcomcnfg.exe]一步打开组件服务。
打开控制面板,查找管理工具,查找组件服务
接下来,我们填写相关信息进行航班预订。
如上所示,预订已成功完成。让我们看看两个数据库中的数据是否正确插入。
在DTC服务中,如果每次提交未中止,提交数量将增加1。当我们配置保留模型时,我们没有将主键设置为标识列,所以如果我们复制主键,我们将再次查看表中的数据。我们提交了三次,但保留主键不重复。第四次,输入主键作为第三次的主键。此时,结果如下:
如果我们验证了leFlightBookings和Reservations表中的数据,新添加的记录将不会显示在其中。这意味着TransactionScope通过将与航班和酒店数据库的连接捆绑在一个范围内来管理事务,并监控提交和中止的事务。
摘要
正如我们在ASP.NET的MVC应用中看到的,当使用EntityFramework实体框架作为概念数据访问层时,总是建议在执行多个数据库操作来存储相关数据时,使用TransactionScope来管理事务。