我在MVC应用程序中找不到如何绑定存储库和所有EF相关数据的正确方法。下面的例子为每个IRepository创建了新的DbContext,这给了我错误
实体对象不能由的多个实例引用IEntityChangeTracker
出现此错误是因为我的实体处于不同的上下文中。例如,代码(它在实体中)将给出错误
var user = new User();
_userRepository.Insert(user)
var order = new Order();
order.User = user;
_orderRepository.Insert(order)
_unitOfWork.Commit();
如果我更改
kernel.Bind(typeof(DbContext)).ToMethod(context => new DbContext(connectionString));
至
kernel.Bind(typeof(DbContext)).ToMethod(context => new DbContext(connectionString)).InRequestScope();
我得到错误
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
如果我在单独的线程中使用ServiceRepository。
也许有人知道解决方案?
var connectionString = ConfigurationManager.ConnectionStrings["Entities"].ConnectionString;
kernel.Bind(typeof(DbContext)).ToMethod(context => new DbContext(connectionString));
kernel.Bind<IObjectSetFactory>().ToMethod(c => kernel.Get<DbContextAdapter>());
kernel.Bind<IObjectContext>().ToMethod(c => kernel.Get<DbContextAdapter>());
kernel.Bind(typeof(IUnitOfWork)).To(typeof(UnitOfWork));
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
kernel.Bind<IServiceRepository>().To<ServiceRepository>();
public interface IServiceRepository
{
UserDetail GetUser(int id);
User GetUser(string email);
User GetUser(string email, string password);
OrderDetail GetOrder(string id);
IEnumerable<OrderDetail> GetOrders(int userId);
IEnumerable<Product> GetProducts();
UserDetail GetParentUser(string partialEmail);
IEnumerable<UserDetail> GetChildUsers(int parentId);
IEnumerable<Statistic> GetStatisticForCurrentMonth(string ip);
void InsertStatistic(QueueItem queueItem);
void InsertStatistic();
void Commit();
void AddUser(User model);
User AddUser(string firstName, string lastName, string email, string password, string country, int? parentId = null, DateTime? dateStamp = null);
void AddOrder(Order order);
void DeleteUser(int id);
void DeleteUser(string email);
bool OrderManager(PaymentProcessorOrder order, out User newUser, out Order newOrder);
User AuthenticatedUser();
string AuthenticatedUserEmail();
bool ValidateUser(string email, string password);
string GetPassword(string email);
}
public class ServiceRepository : IServiceRepository
{
private readonly IRepository<User> _userRepository;
private readonly IRepository<Order> _orderRepository;
private readonly IRepository<UserDetail> _userDetailRepository;
private readonly IRepository<Statistic> _statisticRepository;
private readonly IRepository<Product> _productRepository;
private readonly IRepository<OrderDetail> _orderDetailRepository;
private readonly IUnitOfWork _unitOfWork;
private static readonly object Locker = new object();
public ServiceRepository(IRepository<User> userRepository, IRepository<Statistic> statisticRepository, IRepository<UserDetail> userDetailRepository, IRepository<Order> orderRepository, IUnitOfWork unitOfWork, IRepository<OrderDetail> orderDetailRepository, IRepository<Product> productRepository)
{
_unitOfWork = unitOfWork;
_userRepository = userRepository;
_statisticRepository = statisticRepository;
_userDetailRepository = userDetailRepository;
_orderRepository = orderRepository;
_orderDetailRepository = orderDetailRepository;
_productRepository = productRepository;
}
//Skip code
}
您不应该在ASP.NET应用程序中使用线程,因为它会给您带来很多问题。例如,当IIS回收你的应用程序池或挂起应用程序时,你的线程将被终止,使你的应用处于不一致的状态。或者,线程中未捕获的异常可能会破坏整个应用程序。
通常,您应该在一个单独的服务应用程序中实现异步,这样IIS只需要发送一些消息,这些消息将作为该服务异步处理。这也将解决您的问题,因为您可以为每条消息使用其他DbContext。
如果你真的想使用后台线程,那么你必须为你的请求和线程使用不同的DbContext绑定。例如
kernel.Bind(typeof(DbContext)).ToMethod(context => new DbContext(connectionString)).InRequestScope();
kernel.Bind(typeof(DbContext)).ToMethod(context => new DbContext(connectionString)).WhenInjectedInto<MyTask>();
但这意味着您不能将实体从请求DbContext传递到后台线程上下文。
您不应该将对象从一个DbContext传递到另一个。EF中不支持此操作(分离和附着时除外)。
我建议在每个存储库中使用相同的DbContext,并将存储库保持在同一个线程上。
这里的问题是在处理一个存储库时使用多个存储库,同时也在处理DbContext。您的想法是正确的,您可以使用DbContext(或每个会话)的单个实例
Ninject:Singleton绑定语法?
然而,在处理存储库时,您需要以下内容:
public void Dispose(bool AllResources)
{
if(AllResources)
{
_context.Dispose();
}
//Clean-up other resources
}
你可以使用的另一种方法是将一次性产品放在你的工厂里,这样当你执行多个存储库时,你就会有:
using(var RepoFactory = new RepositoryContextFactory())
{
var repo1 = RepositoryOne(RepoFactory.FetchContext());
var repo2 = RepositoryTwo(RepoFactory.FetchContext());
//Do work
} //Your context would then be disposed of in the closure of the using across all Repositories instead of per repository which will allow you to reuse the same context across multiple repositories.
希望这能在某种程度上有所帮助。