[英]How do you use Moles to mole DbContext from querying the database in EntityFramework 4.1?
[英]How do I share dbContext without CodeFirst in EntityFramework 4.1?
长时间潜伏,第一次海报。
我在这里找到了很多关于如何使用CodeFirst在存储库之间共享dbContext的东西,但我似乎无法将它与我正在处理的项目联系起来,它不使用代码或依赖注入。
首先,关于项目的一点背景,以确保我正在以正确的方式接近这一点。 我进入这个项目,他们首先使用EF4和DB。 我不是EF的专家,但我现在已经摸索了几个不同的项目。
我不得不实施几个不同的要求,迫使我介入他们的“服务”级别和数据库。 换句话说,他们的对象直接调用EF db对象,如
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,以便我可以保持它对相关的更新? 我甚至不知道我是否正确地提出这个问题。 如何在不重新编码整个事物的情况下弥合这一差距?
以下是自打开此问题以来我发现的一些事情。 也许其中一个会做到这一点。
嗯,这个发现肯定更像是重新编码整个事情,但我在寻找关于“做变更跟踪”与触发器响应的链接时发现了这一点。
我刚刚发现这个如何在asp.net中的各种模型库中共享数据上下文,这可能是一种简单的方法来处理它。
我会留下对象上下文的东西。
我在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上下文中,您将共享相同的数据库上下文。 不完全确定这会消除您所获得的错误,但它至少是一种分享您的上下文的方式。
对我来说,答案与我发布的一个链接有关。
当我看到这些类型的注射答案时,让我失望的是语法上它们对我不起作用。 我没有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模式,但我们没有。 但事实证明,我只是必须实施我自己的黑客连接注入的本地化版本! :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.