【问题标题】:Ninject: entity object cannot be referenced by multiple instances of IEntityChangeTrackerNinject:实体对象不能被多个 IEntityChangeTracker 实例引用
【发布时间】:2015-06-24 13:19:07
【问题描述】:

我开始在我的 MVC5 代码优先应用程序中使用 Ninject。这是我的 NinjectWebCommon.cs:

private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            kernel.Bind<CMSContext>()
                .ToSelf()
                //.InSingletonScope();
                .InRequestScope();

            kernel.Bind<IExecutiveRepository>()
                .To<ExecutiveRepository>();

            kernel.Bind<IExecutiveSectionRepository>()
                .To<ExecutiveSectionRepository>();

            kernel.Bind<IExecutiveSectionMappingRepository>()
                .To<ExecutiveSectionMappingRepository>();

            kernel.Bind<IUserRepository>()
                .To<UserRepository>();

            kernel.Bind<IContentRepository>()
                .To<ContentRepository>();

            RegisterServices(kernel);
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

我尝试了 .InSingletonScope() 和 .InRequestScope() 但我仍然收到“entity object cannot be referenced by multiple instances of IEntityChangeTracker”错误。 这是我的界面:

    public interface IExecutiveRepository : IDisposable
{
    IEnumerable<Executive> GetExecutives();
    Executive GetExecutiveById(int executiveId);
    void InsertExecutive(Executive executive);
    void UpdateExecutive(Executive executive);
    void DeleteExecutive(int executiveId);
    void Save();
}

这是我的具体内容:

 public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
    private CMSContext context;

    public ExecutiveRepository(CMSContext context)
    {
        this.context = context;
    }

    public IEnumerable<Executive> GetExecutives()
    {
        return context.Executives.ToList();
    }

    public Executive GetExecutiveById(int id)
    {
        return context.Executives.Find(id);
    }

    public void InsertExecutive(Executive executive)
    {
        context.Executives.Add(executive);
    }

    public void DeleteExecutive(int executiveId)
    {
        Executive executive = context.Executives.Find(executiveId);
        context.Executives.Remove(executive);
    }

    public void UpdateExecutive(Executive executive)
    {
        context.Entry(executive).State = EntityState.Modified;
    }

    public void Save()
    {
        context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

这是控制器(顶部相关部分):

 public class ExecutiveController : Controller
{
    private IExecutiveRepository executiveRepository;
    private IUserRepository userRepository;
    private IExecutiveSectionRepository executiveSectionRepository;
    private IExecutiveSectionMappingRepository executiveSectionMappingRepository;
    private IContentRepository contentRepository;
    private Ninject.IKernel _kernel = new StandardKernel();

    //[Inject]
    public ExecutiveController()
    {
        executiveRepository = _kernel.Get<ExecutiveRepository>();
        userRepository = _kernel.Get<UserRepository>();
        executiveSectionRepository = _kernel.Get<ExecutiveSectionRepository>();
        executiveSectionMappingRepository = _kernel.Get<ExecutiveSectionMappingRepository>();
        contentRepository = _kernel.Get<ContentRepository>();
    }
 ...

不知道我做错了什么,但是在添加一个新的“执行”时它会爆炸......我确实理解它试图使用单独的上下文,这就是问题所在,但我只是不确定如何解决它。显然,NinjectWebCommon.cs 类中的行:

 kernel.Bind<CMSContext>()
                .ToSelf()
                //.InSingletonScope();
                .InRequestScope();

应该是修复,但它不是...... 有什么想法/建议吗?

【问题讨论】:

    标签: c# asp.net-mvc ninject ninject.web.mvc


    【解决方案1】:

    如果您还没有,您应该使用 NUGET 包 Ninject.Web.Mvc。这会将您的应用程序配置为准备好使用 Ninject,而不是您的绑定。从我在您的 CreateKernel() 方法中看到的内容来看,您似乎已经相当熟悉事物的绑定方面。

    一旦你的绑定到位,你不应该在你的控制器中创建内核,这是因为Ninject.Web.Mvc 库配置了 Ninject 来为你创建你的控制器。因此,您添加到它们的任何依赖项都应自动解析。

    所以,你可以使用构造函数注入来解决你的依赖:

    public class ExecutiveController : Controller
    {
        private IExecutiveRepository ExecutiveRepository;
        private IUserRepository UserRepository;
        private IExecutiveSectionRepository ExecutiveSectionRepository;
        private IExecutiveSectionMappingRepository ExecutiveSectionMappingRepository;
        private IContentRepository ContentRepository;
    
        public ExecutiveController(
             IExecutiveRepository executiveRepository,
             IUserRepository userRepository,
             IExecutiveSectionRepository executiveSectionRepository,
             IExecutiveSectionMappingRepository executiveSectionMappingRepository,
             IContentRepository contentRepository)
        {
    
             // Set the field values
             this.ExecutiveRepository = executiveRepository,
             this.UserRepository = userRepository,
             this.ExecutiveSectionRepository = executiveSectionRepository,
             this.ExecutiveSectionMappingRepository = executiveSectionMappingRepository,
             this.ContentRepository = contentRepository;
        }
    
        public ActionResult Index(int id)
        {
            // Use one of your dependencies...
            var executive = this.executiveRepository.GetExecutiveById(id);
        }
    }
    

    或者你可以使用[Inject]属性,效果相同:

    public class ExecutiveController : Controller
    {
        [Inject]
        public IExecutiveRepository executiveRepository { get; set; }
    
        [Inject]
        public IUserRepository userRepository { get; set; }
    
        [Inject]
        public IExecutiveSectionRepository executiveSectionRepository { get; set; }
    
        [Inject]
        public IExecutiveSectionMappingRepository executiveSectionMappingRepository { get; set; }
    
        [Inject]
        public IContentRepository contentRepository { get; set; }
    
        public ExecutiveController()
        {
    
        }
    
        public ActionResult Index(int id)
        {
            // Use one of your dependencies...
            var executive = this.executiveRepository.GetExecutiveById(id);
        }
    }
    

    【讨论】:

    • 抱歉,我遗漏了重要的代码... 1 秒
    • 我现在添加了// Set the field values。如果不设置控制器中的值,它就无法工作。
    • 太棒了。非常感谢你。我以前有过,但学习 Ninject 和存储库模式立刻让我感到困惑。这更澄清了。欣赏它!标记了你。
    • 很高兴能帮上忙。一开始我真的很困惑,我必须承认:)
    • 很高兴不只是我 ;)
    【解决方案2】:

    您正在为每个控制器创建一个内核。

    InRequestScope 仅确保每个请求每个内核一个实例。

    因此,您需要调整内核设置,以便每个 Web 应用程序只有一个内核。见:

    【讨论】:

    • "调整你的内核设置"...这是从 NuGet 来的设置。我所做的只是在 NinjectwebCommon.cs 文件中添加这些绑定行。查看您提供的链接后,Nuget 的版本与您的建议之间似乎存在峡谷大小的裂痕。似乎是从苹果到橘子。我无法从一个到另一个
    • 我收到一条通知,说您添加了更多内容,但文章中没有显示...奇怪。在 SO 的顶部,如果我单击带有红色“1”的图标,它会显示您说“添加 OnActivation(x => {...}); 到绑定。向其添加断点。然后检查是否在那......”......但在实际文章中我什么也没看到...... ???
    • @BatteryBackupUnit 是正确的。您应该在您的应用程序中安装Ninject.Web.Mvc,然后使用推荐的注入方法,例如添加[Inject] 属性或构造函数注入。如果您直接在 MVC 应用程序中创建内核实例(而不是设置绑定),那么您做错了。内核用于设置与接口的绑定,然后自动解析依赖关系。
    • 我现在已经添加了一个答案,我真的希望它有帮助。
    • @Beau 这是我的原始帖子,我在其中提出了一种更接近问题的方法。但后来我发现你在控制器中做了一个new StandardKernel(),这显然是问题所在。所以我编辑了帖子。您现在已经解决了所有问题,所以我不再做进一步的解释;-)
    【解决方案3】:

    这可能无法回答问题。但我倾向于使用 EF 为您提供的IDbContextFactory 并执行以下操作:

    public interface IDefaultContextFactory : IDbContextFactory<CMSContext> {}
    
    public class DefaultContextFactory : IDefaultContextFactory 
    {
        private readonly Lazy<CMSContext> lazyContext = new Lazy<CMSContext>(() => new CMSContext());
    
        public CMSContext Create() 
        {
            return lazyContext.Value;
        }
    }
    

    然后你只需绑定它,当你需要上下文时,你可以这样做:

    public class ExecutiveRepository : IExecutiveRepository, IDisposable
    {
        private readonly CMSContext context;
    
        public ExecutiveRepository(IDefaultContextFactory contextFactory)
        {
            this.context = contextFactory.Create();
        }
    }
    

    我相信@BatteryBackupUnit 是正确的,我也会考虑将上述模式用于上下文。

    【讨论】:

    • 是的,不是很有帮助,但谢谢。我需要像标题所说的那样使用 Ninject。
    • 什么?您正在使用 Ninject.. 这是将上下文注入存储库的更好方法。事实上,这与您使用的依赖注入框架无关
    • 我的办公室/同事使用 Ninject。我必须使用ninject。不是一个选项/选择。它在标题中。如果我的标题是“我正在使用 Ninject,但我想要一些不可知的东西”......那么你的答案会很好。但事实并非如此。
    • 我认为您严重误解了我的意思。这将适用于 ninject 和任何其他依赖注入框架。这只是一种更好的方式来实现您想要实现的目标。
    • @Coulton 哦,我明白了,不,这很酷,我喜欢这个功能!!老实说,这听起来不错。我现在明白你的意思了。
    猜你喜欢
    • 2011-11-01
    • 2011-10-12
    • 1970-01-01
    相关资源
    最近更新 更多