让我们采取简单的存储库(或服务,或经理或你通常称之为的任何东西):
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;
}
}
缺点:太多的情境:)在复杂的过程中可能会出现问题。
我想念一下吗?
感谢您的帮助:)
答案 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
不是线程安全的