我有一个Web应用程序,我刚刚开始使用Entity Framework。我阅读了初学者教程,以及有关Web应用程序每个请求的对象上下文的好处的主题。 但是,我不确定我的背景是否在正确的位置......
我找到了这个非常有用的帖子(Entity Framework Object Context per request in ASP.NET?)并使用了建议的代码:
public static class DbContextManager
{
public static MyEntities Current
{
get
{
var key = "MyDb_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context == null)
{
context = new MyEntities();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
在Global.asax中:
protected virtual void Application_EndRequest()
{
var key = "MyDb_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context != null)
{
context.Dispose();
}
}
然后,我在我的网页中使用它:
public partial class Login : System.Web.UI.Page
{
private MyEntities context;
private User user;
protected void Page_Load(object sender, EventArgs e)
{
context = DbContextManager.Current;
if (Membership.GetUser() != null)
{
Guid guid = (Guid)Membership.GetUser().ProviderUserKey;
user = context.Users.Single(u => (u.Id == guid));
}
}
protected void _Button_Click(object sender, EventArgs e)
{
Item item = context.Items.Single(i => i.UserId == user.Id);
item.SomeFunctionThatUpdatesProperties();
context.SaveChanges();
}
}
我读了很多,但这对我来说仍然有点困惑。 Page_Load中的上下文getter是否正常?我仍然需要使用“使用”或使用Global.asax方法处理吗?
如果我感到困惑,我很抱歉,如果有人能帮我理解它应该在哪里,我真的非常感激。
非常感谢!
按照母语回答和评论进行编辑:
这是DbContextManager:
public static class DbContextManager
{
public static MyEntities Current
{
get
{
var key = "MyDb_" + typeof(MyEntities).ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context == null)
{
context = new MyEntities();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
页面:
public partial class Login : System.Web.UI.Page
{
private User user;
protected void Page_Load(object sender, EventArgs e)
{
if (Membership.GetUser() != null)
{
Guid guid = (Guid)Membership.GetUser().ProviderUserKey;
user = UserService.Get(guid);
}
}
protected void _Button_Click(object sender, EventArgs e)
{
if (user != null)
{
Item item = ItemService.GetByUser(user.Id)
item.SomeFunctionThatUpdatesProperties();
ItemService.Save(item);
}
}
}
和ItemService类:
public static class ItemService
{
public static Item GetByUser(Guid userId)
{
using (MyEntities context = DbContextManager.Current)
{
return context.Items.Single(i => (i.UserId == userId));
}
}
public static void Save(Item item)
{
using (MyEntities context = DbContextManager.Current)
{
context.SaveChanges();
}
}
}
答案 0 :(得分:1)
我认为您应该阅读有关EntityFramework和UnitofWork模式的存储库模式的更多信息。
Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC
我知道这是mvc,你可能正在使用网络表单,但你可以了解如何实现它。
在每个请求上处理上下文有点奇怪,因为可能会有请求您不会触及数据库,因此您将执行不必要的代码。
您应该做的是获取一个数据访问层,并实现一个存储库模式,您将访问页面背后代码所需的任何方法。
答案 1 :(得分:1)
我不会依赖Thread.CurrentContext
财产。
首先,Microsoft说,Context
类不能直接在您的代码中使用:
https://msdn.microsoft.com/en-us/library/system.runtime.remoting.contexts.context%28v=vs.110%29.aspx
其次,假设你想对数据库进行异步调用。
在这种情况下,我们会构建一个额外的MyEntities
个实例,并且 将被置于Application_EndRequest
。
此外,ASP.NET本身并不保证在执行请求时不切换线程。 我有一个类似的问题,看看这个:
is thread switching possible during request processing?
我会改用"MyDb_" + typeof(MyEntities).ToString()
。
在Application_EndRequest
中处理数据库上下文是可以的,但它会产生一点性能损失,因为您的上下文将不会停留超过需要的时间,最好尽快关闭它(您实际上,不需要一个开放的上下文来呈现页面,对吗?)
例如,如果您使用存储库模式,并且多个存储库在执行请求时共享相同的数据库上下文。
最后,您调用SaveChanges
,并且不同存储库所做的所有更改都将在单个事务中提交。
但是在您的示例中,您直接从您网页的代码中调用数据库,在这种情况下,我没有看到任何直接使用using
创建上下文的理由。
希望这有帮助。
更新:每个请求具有上下文的示例:
//Unit of works acts like a wrapper around DbContext
//Current unit of work is stored in the HttpContext
//HttpContext.Current calls are kept in one place, insted of calling it many times
public class UnitOfWork : IDisposable
{
private const string _httpContextKey = "_unitOfWork";
private MyContext _dbContext;
public static UnitOfWork Current
{
get { return (UnitOfWork) HttpContext.Current.Items[_httpContextKey]; }
}
public UnitOfWork()
{
HttpContext.Current.Items[_httpContextKey] = this;
}
public MyEntities GetContext()
{
if(_dbContext == null)
_dbContext = new MyEntities();
return _dbContext;
}
public int Commit()
{
return _dbContext != null ? _dbContext.SaveChanges() : null;
}
public void Dispose()
{
if(_dbContext != null)
_dbContext.Dispose();
}
}
//ContextManager allows repositories to get an instance of DbContext
//This implementation grabs the instance from the current UnitOfWork
//If you want to look for it anywhere else you could write another implementation of IContextManager
public class ContextManager : IContextManager
{
public MyEntities GetContext()
{
return UnitOfWork.Current.GetContext();
}
}
//Repository provides CRUD operations with different entities
public class RepositoryBase
{
//Repository asks the ContextManager for the context, does not create it itself
protected readonly IContextManager _contextManager;
public RepositoryBase()
{
_contextManager = new ContextManager(); //You could also use DI/ServiceLocator here
}
}
//UsersRepository incapsulates Db operations related to User
public class UsersRepository : RepositoryBase
{
public User Get(Guid id)
{
return _contextManager.GetContext().Users.Find(id);
}
//Repository just adds/updates/deletes entities, saving changes is not it's business
public void Update(User user)
{
var ctx = _contextManager.GetContext();
ctx.Users.Attach(user);
ctx.Entry(user).State = EntityState.Modified;
}
}
public class ItemsRepository : RepositoryBase
{
public void UpdateSomeProperties(Item item)
{
var ctx = _contextManager.GetContext();
ctx.Items.Attach(item);
var entry = ctx.Entry(item);
item.ModifiedDate = DateTime.Now;
//Updating property1 and property2
entry.Property(i => i.Property1).Modified = true;
entry.Property(i => i.Property2).Modified = true;
entry.Property(i => i.ModifiedDate).Modified = true;
}
}
//Service encapsultes repositories that are necessary for request handling
//Its responsibility is to create and commit the entire UnitOfWork
public class AVeryCoolService
{
private UsersRepository _usersRepository = new UsersRepository();
private ItemsRepository _itemsRepository = new ItemsRepository();
public int UpdateUserAndItem(User user, Item item)
{
using(var unitOfWork = new UnitOfWork()) //Here UnitOfWork.Current will be assigned
{
_usersRepository.Update(user);
_itemsRepository.Update(user); //Item object will be updated with the same DbContext instance!
return unitOfWork.Commit();
//Disposing UnitOfWork: DbContext gets disposed immediately after it is not longer used.
//Both User and Item updates will be saved in ome transaction
}
}
}
//And finally, the Page
public class AVeryCoolPage : System.Web.UI.Page
{
private AVeryCoolService _coolService;
protected void Btn_Click(object sender, EventArgs e)
{
var user = .... //somehow get User and Item objects, for example from control's values
var item = ....
_coolService.UpdateUserAndItem(user, item);
}
}