在一个工作单元上准备多个EF上下文-TransactionScope [英] Preparing for multiple EF contexts on a unit of work - TransactionScope

查看:232
本文介绍了在一个工作单元上准备多个EF上下文-TransactionScope的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在考虑实现用于处理多个数据源的单个工作单元的选项-实体框架。我想出了一种暂定的方法-现在只处理单个上下文-但这显然不是一个好主意。

I'm thinking of the options in regards to implementing a single unit of work for dealing with multiple datasources - Entity framework. I came up with a tentative approach - for now dealing with a single context - but it apparently isn't a good idea.

如果我们要分析下面的代码,您会认为这是一个不好的实现吗?

If we were to analyze the code below, would you consider it a bad implementation? Is the lifetime of the transaction scope a potential problem?

当然,如果我们用不同的上下文包装事务范围,那么第二个上下文将被覆盖。失败...

Of course if we wrap the transaction scope with different contexts we'd be covered if the second context.SaveChanges() failed...

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Transactions;

    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                using(UnitOfWork unitOfWork = new UnitOfWork())
                {

                    var repository = new EmployeeRepository(unitOfWork);

                    var employee = repository.CreateOrGetEmployee("Whatever Name");

                    Console.Write(employee.Id);

                    unitOfWork.SaveChanges();
                }
            }
        }

        class UnitOfWork : IDisposable
        {
            TestEntities _context;
            TransactionScope _scope;
            public UnitOfWork()
            {
                _scope = new TransactionScope();
                _context = new TestEntities();
            }

            public void SaveChanges()
            {
                _context.SaveChanges();
                _scope.Complete();
            }

            public TestEntities Context
            {
                get
                {
                    return _context;
                }
            }

            public void Dispose()
            {
                _scope.Dispose();
                _context.Dispose();
            }
        }

        class EmployeeRepository
        {
            UnitOfWork _unitOfWork;

            public EmployeeRepository(UnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
            }

            public Employee GetEmployeeById(int employeeId)
            {
                return _unitOfWork.Context.Employees.SingleOrDefault(e => e.Id == employeeId);
            }

            public Employee CreateEmployee(string fullName)
            {
                Employee employee = new Employee();
                employee.FullName = fullName;
                _unitOfWork.Context.SaveChanges();
                return employee;
            }

            public Employee CreateOrGetEmployee(string fullName)
            {
                var employee = _unitOfWork.Context.Employees.FirstOrDefault(e => e.FullName == fullName);
                if (employee == null)
                {
                    employee = new Employee();
                    employee.FullName = fullName;
                    this.AddEmployee(employee);
                }
                return employee;
            }

            public Employee AddEmployee(Employee employee)
            {
                _unitOfWork.Context.Employees.AddObject(employee);
                _unitOfWork.Context.SaveChanges();
                return employee;
            }
        }
    }


推荐答案

为什么要在构造函数中启动 TransactionScope

Why do you start TransactionScope in constructor? You need it only for saving changes.

public void SaveChanges()
{
    // SaveChanges also uses transaction which uses by default ReadCommitted isolation
    // level but TransactionScope uses by default more restrictive Serializable isolation
    // level 
    using (var scope = new TransactionScope(TransactionScopeOption.Required,
                                            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        _context.SaveChanges();
        scope.Complete();
    }
}

如果您想拥有具有更多上下文的工作单元您只需将所有这些上下文包装在相同的工作类单元中。您的 SaveChanges 会变得有些复杂:

If you want to have unit of work with more contexts you will simply wrap all those context in the same unit of work class. Your SaveChanges will become little bit more complicated:

public void SaveChanges()
{
    using (var scope = new TransactionScope(TransactionScopeOption.Required,
                                            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        _contextA.SaveChanges(SaveOptions.DetectChangesBeforeSave);
        _contextB.SaveChanges(SaveOptions.DetectChangesBeforeSave);
        scope.Complete();
        _contextA.AcceptAllChanges();
        _contextB.AcceptAllChanges(); 
    }
}

此版本将保存操作与重置内部状态分开上下文。原因是,如果第一个上下文成功保存更改,但第二个引发异常,则事务将回滚。因此,我们不希望第一个上下文已经清除所有接受的更改(我们将丢失有关已执行更改的信息,我们将无法再次保存它们)。

This version separate saving operation from reseting inner state of the context. The reason is that if the first context successfully saves changes but the second fires exception the transaction will be rolled back. Because of that we don't want the first context to have already cleared all changes as accepted (we would lose information about performed changes and we will not be able to save them again).

这篇关于在一个工作单元上准备多个EF上下文-TransactionScope的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆