1

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
}
4

3 に答える 3

2

多くの問題が発生するため、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 からバックグラウンド スレッド コンテキストにエンティティを渡すことができないことを意味します。

于 2012-07-25T15:29:58.023 に答える
0

ここでの問題は、1つを破棄するときに複数のリポジトリを使用することであり、DbContextも破棄します。DbContextのシングルトンインスタンスを(またはセッションごとに)使用できると考えるのは正しいことです。

Ninject:シングルトンバインディング構文?

ただし、リポジトリを破棄するには、次のようなものが必要です。

public void Dispose(bool AllResources)
{
     if(AllResources)
     {
          _context.Dispose();
     }
     //Clean-up other resources
}

使用できる他のアプローチの1つは、Disposableをファクトリに配置して、複数のリポジトリを実行するときに次のようにすることです。

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.

うまくいけば、これはあるレベルで役立つでしょう。

于 2012-07-25T15:11:45.803 に答える
0

ある DbContext から別の DbContext にオブジェクトを渡すべきではありません。これは EF ではサポートされていません (デタッチしてアタッチする場合を除く)。

すべてのリポジトリで同じ DbContext を使用し、リポジトリを同じスレッドに保持することをお勧めします。

于 2012-07-25T14:53:44.190 に答える