长时间潜伏,第一次海报。
我在这里找到了很多关于如何使用CodeFirst在存储库之间共享dbContext的东西,但我似乎无法将其与我正在处理的项目相关联,它不使用代码或依赖注入
首先,关于项目的一些背景知识,以确保我正确地接近这一点。我进入这个项目,他们首先使用EF4和DB。我不是EF的专家,但我现在在几个不同的项目中摸索着。
我不得不实施几个不同的要求,迫使我介入他们的“服务”级别和数据库。换句话说,他们的对象直接调用EF数据库对象,如
using (var db = new MYDB()){
var bar = db.Foo
.Include("Transactions")
.Include("blah")
.Where(...);
//do stuff
db.SaveChanges();
}
我必须做的一件事就是跟踪所有变化的字段,所以我抽回了一个级别,现在我们已经
FooObject bar = GetFooObject(...);
bar.Title = "asdfasdf";
//do stuff to bar
bar.Save();
将所有字段包装到属性中,以便我可以注销任何更改。在bar.save中,我打开一个db上下文,获取现有的Foo或创建一个新的,分配所有的值,然后调用db.SaveChanges。
事实证明,他们也会根据交易和blah做很多子查询。所以当他们做类似的事情时
var bar = GetFooObject(...);
var t = new Transaction();
//do stuff to t
...
bar.Transactions.Add(t);
bar.Save();
我遇到各种上下文错误,说dbcontext不再可用等等。我完全理解。我不知道的是如何解决它。我已经看到很多关于在使用之前创建dbContext然后传入它的东西,但是我似乎无法找到正确的方法来完成它以便它可以使用我的代码。
我最近的尝试基于几个关于如何将DBContext转换为ObjectContext的例子(这又是基于我发现的所有关于共享连接的所有示例引用了ObjectContext而不是DBContext的事实)看起来像这样:
using (var db = ((IObjectContextAdapter)(new FooDB())).ObjectContext)
{
using (var context = new DbContext(db, false))
{
var bar = FooObject.GetFooObject(fooId);
Result r = bar.ProcTrans(amount,
transDate,
db.TransactionTypes
.Include(tt => tt.Description)
.SingleOrDefault(tt => tt.TypeID == transactionTypeId),
employeeId,
comment);
但是使用这段代码我得到一个错误,我没有为TransactionTypes定义。它无法识别我的任何数据库对象。
如何创建DBContext并将其传递给我的FooObject,以便我可以将其保持打开以进行相关更新?我甚至不知道我是否正确地提出这个问题。如何在不重新编码整个事情的情况下弥合这一差距?
以下是我打开这个问题后发现的一些事情。也许其中一个会做到这一点。
嗯,这个发现肯定更像是重新编码整个事情,但我在找到关于“做变更跟踪”和触发器响应的链接时发现了这一点。
poco in the entity framework part-3: change tracking with poco
我刚发现这个how do I share a data context across various model repositories in asp.net可能是一种简单的方法。
答案 0 :(得分:1)
我会留下对象上下文的内容。
我在MVC应用程序中实现共享DBContext的方式是这样的:
public class BaseRepository
{
public static MyAppContext GetDataContext()
{
string ocKey = "ocm_" + HttpContext.Current.GetHashCode().ToString("x");
if (!HttpContext.Current.Items.Contains(ocKey))
HttpContext.Current.Items.Add(ocKey, new MyAppContext());
return HttpContext.Current.Items[ocKey] as MyAppContext;
}
}
然后每当我需要进行数据库操作时,我都可以调用:
BaseRepository.GetDataContext().YourObjects.Where(x => ...);
....
BaseRepository.GetDataContext().SaveChanges();
只要您仍处于相同的HTTP上下文中,您将共享相同的数据库上下文。不完全确定这会消除您所获得的错误,但它至少是分享您的背景的一种方式。
答案 1 :(得分:0)
对我来说,答案与我发布的其中一个链接有关。
how do I share a data context across various model repositories in asp.net
当我看到这些类型的注射答案时,让我失望的是语法上它们对我不起作用。我没有DataContext,也没有任何Repository模型,但我决定在概念上尝试一下并在各处传递Context。
基本上,我传递了与Object构造函数或创建新对象的任何工厂方法的连接,并将其存储在局部变量中,就像这样排序。
public class Foo{
private MyDB _db;
private Foo _foo;
public FooObject(MyDB dbContext)
{
_db = dbContext;
}
public static FooObject GetFooObject(int FooID, MyDB db){
bool closeFlag = false;
//if null was passed in, then we will create our own connection and manage it
if (db == null)
{
_db = new MyDB();
closeFlag = true;
} else {
//otherwise, we set our local variable
_db = db;
}
//from now on, all queries are done using the local variable
var _f = _db.Foos
.Include("x")
.Include("y")
.Include("z")
.SingleOrDefault(f => f.FooID == FooID);
var fo = FooObjectFromFoo(_f, db);
if (closeFlag)
db.Dispose();
return fo;
}
// This copies all of the values from Foo and puts the into a FooObject
public static FooObject FooObjectFromFoo(Foo f, MyDB dbContext){
if (l == null)
return null;
// note that we pass the dbContext to the constuctor
FooObject _f = new FooObject(dbContext){
_foo = f,
...
//note x, y, and z are the other EF "table references". I'm not sure what you technically call them.
x = f.x,
y = f.y,
z = f.z
};
return _f;
}
//we call this to save the changes when we're done
public bool Save(){
bool close = false;
bool retval = true;
MyDB db = _db;
//remember we set _db in the constructor
if (db == null) {
db = new MyDB();
close = true;
}
try
{
// a reference to this row should have been saved in _foo if we loaded it from the db.
// take a look at FooObjectFromFoo
if (_foo == null)
{
_foo = db.Foos.SingleOrDefault(x => x.FooID == _FooID);
}
if (_foo == null)
{
_foo = new Foo();
}
//copy all my object values back to the EF Object
_foo.blah = blah;
_foo.x = x;
_foo.y = y;
try
{
//save the new one.
db.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
TransactionResult.AddErrors(dbEx);
retval = false;
}
}
catch { throw new Exception("Something went wrong here.");}
finally { if (close) db.Dispose(); } //if we created this connection then let's close it up.
}
}
现在在我的方法中,我总是使用本地_db连接。在我的FooObject之外,我们有一个FooService,它是从所有控制器调用的。因此,当实例化FooService时,我使用下面的类创建数据库连接。 如果我理解正确,这应该给我一个在我的服务请求期间存在的上下文,在我的情况下,相当可靠地模仿请求。
namespace My.Domain
{
public class MyDataContext : IDisposable {
private MyDB _context;
private bool _ownContext;
public MyDataContext(){
_context = new MyDB();
_ownContext = true;
}
public MyDataContext(MyDB db)
{
_context = db;
_ownContext = false;
}
public MyDB Context
{
get { if (_context == null) { _context = new MyDB(); _ownContext = true; } return _context; }
set { _context = value; }
}
public bool OwnContext
{
get { return _ownContext; }
set { _ownContext = value; }
}
public void Dispose()
{
if (_context != null && _ownContext)
_context.Dispose();
}
}
}
在FooService中,我做的就像这样。
private MyDb db;
public FooService (){
var _db = new MyDataContext();
db = _db.Context;
}
public Result ProcessTransaction(int FooId, string comment)
{
var foo = FooObject.GetFooObject(FooId,db);
Result r = foo.ProcessTransaction(comment);
if (r.Success)
foo.Save();
return r;
}
我认为这样做“正确”我应该只在关闭上下文时保存更改...但我已经在我的FooObject上有一个Save方法,所以我只是在那里调用db.SaveChanges。
我知道有很多方法可以改善这一点,我确信随着时间的推移我会实施其中一些,但就目前而言,这就是诀窍。这就是我解决所有“上下文不再可用”的问题,而且这个对象来自不同的上下文错误。
在查看其他人的例子时,让我感到震惊的是他们都在使用CodeFirst和某种依赖注入。他们通常使用Repository模式,但我们没有。但事实证明,我只是必须实施我自己的黑客连接注入的本地化版本! :)