对象上下文,存储库和事务

时间:2009-06-29 00:22:04

标签: c# entity-framework transactions

我想知道在实体框架中使用转换的最佳方法是什么。

说我有三个存储库:

 Repo1(ObjectContext context)
 Repo2(ObjectContext context)
 Repo3(ObjectContext context)

以及一个包含三个存储库的服务对象:

 Service(Repo1 repo1,Repo2 repo2, Repo3 repo3)
 Serive.CreateNewObject <- calls repo1, repo2, repo3 to do stuff.

因此,当我创建服务时,我先创建三个存储库并将其传递下来,每个存储库都会获取一个对象上下文,因此我的代码看起来像这样:

MyObjectContext context = new MyObjectContext();
Repo1 repo = new Repo1(context);
// etc

现在我有一个控制器类,负责调用我的应用程序的不同服务和组件,显示正确的表单等。现在我想要做的是将一个控制器方法中发生的一切包装在一个交易,以便如果出现问题我可以回滚。

控制器接受一些不同的Service对象,但对对象上下文一无所知。

我的问题是:

  1. 是否应将上下文传递到服务层。
  2. 如何在控制器中实现事务,以便在服务中发生任何事情 层层没有提交,直到一切都过去了。
  3. 很抱歉,如果有点难以理解......

4 个答案:

答案 0 :(得分:3)

为什么你的控制器不知道ObjectContext?

这是我想说的。签出 - http://msdn.microsoft.com/en-us/magazine/dd882510.aspx - 这里的命令是提交/回滚UnitOfWork(ObjectContext)的内容。

如果您不想让控制器知道完全关于EF(优秀设计),那么您希望将ObjectContext抽象为类似于上述链接中的方法的接口。

答案 1 :(得分:3)

如何使用自定义TransactionScope,即在所有服务都已提交时提交的事务?

public class TransactionScope : Scope<IDbTransaction>
{
    public TransactionScope()
    {
        InitialiseScope(ConnectionScope.CurrentKey);
    }

    protected override IDbTransaction CreateItem()
    {
        return ConnectionScope.Current.BeginTransaction();
    }

    public void Commit()
    {
        if (CurrentScopeItem.UserCount == 1)
        {
            TransactionScope.Current.Commit();
        }
    }
}

因此,只有在UserCount为1时才会提交事务,这意味着最后一项服务已提交。

范围类是(羞耻我们不能做附件......):

public abstract class Scope<T> : IDisposable
    where T : IDisposable
{
    private bool disposed = false;

    [ThreadStatic]
    private static Stack<ScopeItem<T>> stack = null;

    public static T Current
    {
        get { return stack.Peek().Item; }
    }

    internal static string CurrentKey
    {
        get { return stack.Peek().Key; }
    }

    protected internal ScopeItem<T> CurrentScopeItem
    {
        get { return stack.Peek(); }
    }

    protected void InitialiseScope(string key)
    {
        if (stack == null)
        {
            stack = new Stack<ScopeItem<T>>();
        }

        // Only create a new item on the stack if this
        // is different to the current ambient item
        if (stack.Count == 0 || stack.Peek().Key != key)
        {
            stack.Push(new ScopeItem<T>(1, CreateItem(), key));
        }
        else
        {
            stack.Peek().UserCount++;
        }            
    }

    protected abstract T CreateItem();

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // If there are no users for the current item
                // in the stack, pop it
                if (stack.Peek().UserCount == 1)
                {
                    stack.Pop().Item.Dispose();
                }
                else
                {
                    stack.Peek().UserCount--;
                }
            }

            // There are no unmanaged resources to release, but
            // if we add them, they need to be released here.
        }

        disposed = true;
    }
}

public class ScopeItem<T> where T : IDisposable
{
    private int userCount;
    private T item;
    private string key;

    public ScopeItem(int userCount, T item, string key)
    {
        this.userCount = userCount;
        this.item = item;
        this.key = key;
    }


    public int UserCount
    {
        get { return this.userCount; }
        set { this.userCount = value; }
    }

    public T Item
    {
        get { return this.item; }
        set { this.item = value; }
    }

    public string Key
    {
        get { return this.key; }
        set { this.key = value; }
    }
}

public class ConnectionScope : Scope<IDbConnection>
{
    private readonly string connectionString = "";
    private readonly string providerName = "";

    public ConnectionScope(string connectionString, string providerName)
    {
        this.connectionString = connectionString;
        this.providerName = providerName;

        InitialiseScope(string.Format("{0}:{1}", connectionString, providerName));
    }

    public ConnectionScope(IConnectionDetailsProvider connectionDetails)
        : this(connectionDetails.ConnectionString, connectionDetails.ConnectionProvider)
    {
    }

    protected override IDbConnection CreateItem()
    {
        IDbConnection connection = DbProviderFactories.GetFactory(providerName).CreateConnection();
        connection.ConnectionString = connectionString;
        connection.Open();
        return connection;
    }
}

答案 2 :(得分:1)

将操作包裹在TransactionScope

答案 3 :(得分:0)

您可能希望实现Workflow Foundation使用的事务模型。它基本上有一个所有“组件”实现的接口。在每次成功完成主要工作之后,主机在每个上面调用“commit”方法。如果一个失败,它会调用“回滚”方法。