实体框架,具有同时读/写能力的存储库/服务

时间:2017-03-23 12:39:39

标签: c# entity-framework entity-framework-6

让我们采取简单的存储库(或服务,或经理或你通常称之为的任何东西):

public class Repository
{
    Context context = new Context();

    public void Add()
    {
        context.Products.Add(new Product());
        context.SaveChanges();
    }
    public void AddEvent()
    {
        context.Events.Add(new Event());
        context.SaveChanges();
    }
    public IQueryable<Product> GetAll()
    {
        return context.products;
    }
}

public class TheClass
{
    public void Run()
    {
        foreach (var product in Repository.GetAll())
            Repository.AddEvent();
    }
}

执行Run(),我们将获得异常3988.我们无法同时执行读取循环和刷新更改。好。 但同时在庞大的项目中,当我们需要保存或不在循环结束后(交叉调用,API调用,可能的异常,多线程,运气不好等等)时它不太清楚。所以我正在寻找一些&# 34;最佳实践&#34;以相同的方式组织这些类,并使它们安全,而不会从中获取逻辑。

目前我已经找到了一些解决方案:

1。块。 优点:代码看起来很好,可能是查询的一部分。 缺点:多个SQL事务。在巨大的查询中,它可能是性能上的痛苦。光标位置将失败(不再分页:))

2。 .ToList() 优点:? 缺点:不适用于大数据,性能。

第3。使用不同的交易进行保存操作 还没有尝试过。只是想法,可能是错误的方式。

4。 (这是我目前正在使用的)使用&#34;安全保存&#34;为经理(存储库/服务)创建基类。功能即可。它尝试保存,如果读取事务正在进行中=处理其异常并且标记为在dispose上保存(或者标志可以在创建时设置)

public abstract class DataManagerBase<T> : IDisposable where T : DbContext
{
    protected T Context;

    bool SaveOnDispose;
    public DataManagerBase(bool saveOnDispose = false)
    {
        if (!typeof(T).IsSubclassOf(typeof(DbContext)))
            throw new Exception("Wrong class in dbcontext");
        Context = (T)Activator.CreateInstance(typeof(T));
    }

    public int SaveChanges()
    {
        if (!SaveOnDispose)
        {
            try
            {
                return Context.SaveChanges();
            }
            catch (SqlException e) when (e.Number == 3988)
            {
                SaveOnDispose = true;
            }
        }
        return 0;
    }

    public void Dispose()
    {
        try
        {
            if (SaveOnDispose && Context != null)
                try
                {
                    Context.SaveChanges();
                }
                catch (Exception e)
                {
                }

            Context.Dispose();
        }
        catch (Exception e)
        {
            throw new Exception("The world has fall apart ...");
        }
    }
}

优点:它有效。 缺点:在保存期间无法处理错误(如果保存将在处置时) - &gt;无法进行回滚(使回调看起来不太好),不能保证在上下文生命周期内跟踪更改。

5。为读/写操作创建不同的上下文 缺点:根本没有变化跟踪。

6。为每个操作创建新的上下文,如下所示:

public void Add()
    {
        using (var context = new DbContext())
        {
            context.Products.Add(new Product());
            context.SaveChanges();
        }
    }

    public void AddEvent()
    {
        using (var context = new DbContext())
        {
            context.Events.Add(new Events());
            context.SaveChanges();
        }
    }

    public IQueryable<Product> GetAll()
    {
        using (var context = new DbContext())
        {
            return context.products;
        }
    }

缺点:太多的情境:)在复杂的过程中可能会出现问题。

我想念一下吗?

感谢您的帮助:)

1 个答案:

答案 0 :(得分:0)

  

您不需要单独context进行读写操作。

public IQueryable<Product> GetAll()
{
    return context.products;
}
  

IQueryable接口继承IEnumerable接口,以便if   它表示一个查询,可以枚举该查询的结果。   枚举导致与IQueryable关联的表达式树   要执行的对象

所以你需要执行它。

//Approach 1
public void Run()
{
    var products = Repository.GetAll().ToList();
    foreach (var product in products)
        Repository.AddEvent();
}

//Having condition in IQuerable
public void Run()
{
    var products = Repository
                  .GetAll();
                  .Where(someCondition)
                  .OrderBy(SomeOrder)
                  .ToList(); //At this point ef will materialize the query 
                  //and process in sql and you will get the 
                  //result applying this condition at SQL.
    foreach (var product in products)
        Repository.AddEvent();
}

赞成  
  - 它不会有多个往返数据库的往返   - 它将一次性获取数据

缺点  
  - 它将数据提取到内存

无论如何,如果你进行迭代,你会把它加载到内存中,所以最好避免昂贵的I / O操作。

在开始新交易之前,您必须关闭与连接关联的阅读器。msdn

请记住EntityFramework Context不是线程安全的