简体   繁体   English

Ninject,MVC5,EF6,存储库+ UoW

[英]Ninject, MVC5, EF6, Repository + UoW

I'm having two problems, the first is scaling and became visible while load testing. 我遇到两个问题,第一个是缩放,并且在负载测试时可见。

Under load, things quickly (10 concurrent or less) fail with the error: 在负载下,很快(10个并发以下)失败并显示以下错误:

There is already an open DataReader associated with this Command which must be closed first. 已经有一个与此命令相关联的打开的DataReader,必须先关闭它。

Or 要么

ExecuteReader requires an open and available Connection. ExecuteReader需要一个开放且可用的连接。 The connection's current state is open. 连接的当前状态为打开。

The stack trace references a repository every time, example: 堆栈跟踪每次都引用一个存储库,例如:

Line 23:         public AboutViewDto GetAboutView()
Line 24:         {
Line 25:             var featured = UnitOfWork.FaqRepository
Line 26:                 .GetFeatured()

Original NinjectDependencyResolver: 原始NinjectDependencyResolver:

    public class NinjectDependencyResolver 
        : IDependencyResolver, System.Web.Mvc.IDependencyResolver
    {
        private readonly IKernel _kernel;

        public NinjectDependencyResolver() : this(new StandardKernel())
        {

        }

        public NinjectDependencyResolver(IKernel kernel)
        {
            _kernel = kernel;
            AddBindings(_kernel);
        }

        public IDependencyScope BeginScope()
        {
            return this;
        }

        public object GetService(Type serviceType)
        {
            return _kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _kernel.GetAll(serviceType);
        }

        public void Dispose()
        {
            // nothing?? 
        }

        private static void AddBindings(IBindingRoot kernel)
        {

            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InSingletonScope();
            kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();

            kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InSingletonScope();
            kernel.Bind<IFaqRepository>().To<FaqRepository>().InSingletonScope();
            kernel.Bind<IListValueRepository>().To<ListValueRepository>().InSingletonScope();
            kernel.Bind<INoticeRepository>().To<NoticeRepository>().InSingletonScope();
            kernel.Bind<IOrganizationRepository>().To<OrganizationRepository>().InSingletonScope();
            kernel.Bind<ITagRepository>().To<TagRepository>().InSingletonScope();

            kernel.Bind<IAdminService>().To<AdminService>();
            kernel.Bind<IAutoMapperService>().To<AutoMapperService>();
            kernel.Bind<IHomeService>().To<HomeService>();
            kernel.Bind<IInfoService>().To<InfoService>();
            kernel.Bind<IMailService>().To<MailService>();
            kernel.Bind<INoticeService>().To<NoticeService>();
            kernel.Bind<IOrganizationService>().To<OrganizationService>();
            kernel.Bind<ISearchService>().To<SearchService>();

            kernel.Bind<IValidator<QuestionDto>>().To<QuestionDtoValidator>();
            kernel.Bind<IValidator<NoticeCommentDto>>().To<CommentDtoValidator>();
            kernel.Bind<IValidator<NoticeContactDto>>().To<NoticeContactDtoValidator>();
            kernel.Bind<IValidator<NoticeDto>>().To<NoticeDtoValidator>();
            kernel.Bind<IValidator<OrganizationDto>>().To<OrganizationDtoValidator>();

        }
    }
}

I had a hunch that InSingletonScope() was causing the problem so I changed it to: 我有一种直觉,认为InSingletonScope()会引起问题,所以我将其更改为:

kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InRequestScope();

And changed all of the other SingletonScopes to RequestScope. 并将所有其他SingletonScopes更改为RequestScope。

After making that change, the site handles 400+ concurrent users without any failures, however... 进行更改之后,该站点可以处理400多个并发用户,而不会发生任何故障,但是...

Now no commits work against the database. 现在,没有提交可针对数据库进行。 I can trace the calls through the controller, to the service, to the repository, and to the DBContext commit, but the inserts or updates are not happening. 我可以通过控制器,服务,存储库和DBContext提交跟踪调用,但是不会发生插入或更新。

I'll post snippets of each item here in hopes someone can spot the dumb error we've made or suggest improvements. 我将在此处发布每个项目的摘要,希望有人能发现我们所做的愚蠢错误或提出改进建议。

Snippets follow: 片段如下:

Activity, update a Notice, everything involved: 活动,更新通知,涉及的所有内容:

1) Ninject: 1)注入:

kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
kernel.Bind<INoticeService>().To<NoticeService>();
 .. etc...

2) Controller: 2)控制器:

public sealed class NoticeController : BaseController
{
    public NoticeController(IAutoMapperService autoMapperService, INoticeService noticeService)
    {
        AutoMapperService = autoMapperService;
        NoticeService = noticeService;
    }
...

3) NoticeService 3)NoticeService

public class NoticeService : BaseService, INoticeService
{
    public NoticeService(
        IUnitOfWork unitOfWork, 
        IAutoMapperService autoMapperService,
        IValidator<NoticeDto> noticeDtoValidator)
        : base(unitOfWork)
    {
        AutoMapperService = autoMapperService;
        NoticeDtoValidator = noticeDtoValidator;
    }

4) Unit of Work 4)工作单位

public class UnitOfWork : IUnitOfWork
{
    private IDataContext _dataContext;
    private bool _disposed;
    private ObjectContext _objectContext;
    private DbTransaction _transaction;

    public UnitOfWork(
        IDataContext dataContext,
        INoticeRepository noticeRepository)
    {
        _dataContext = dataContext;
        NoticeRepository = noticeRepository;
    }

5) Notice Repository 5)通知库

public class NoticeRepository : Repository<Notice>, INoticeRepository
{
   public NoticeRepository(IDataContext context) : base(context)
   {

   }
...

6) Controller Action 6)控制器动作

    public ActionResult Create(NoticeViewModel notice)
    {
        notice.TypeId = Convert.ToInt32(NoticeType.ManuallyEnteredDocument);
        var newNotice = AutoMapperService.Map<NoticeViewModel, NoticeDto>(notice);
        NoticeService.Create(newNotice);
        return RedirectToAction("Details", new { name = notice.Arc });
    }

7) NoticeService.Create(new): 7)NoticeService.Create(新):

    public void Create(NoticeDto notice)
    {
        NoticeDtoValidator.ValidateAndThrow(notice);
        var newNotice = AutoMapperService.Map<NoticeDto, Notice>(notice);
        UnitOfWork.NoticeRepository.Add(newNotice);
        UnitOfWork.SaveChanges();
    }

8) Generic Repository Add(): 8)通用存储库Add():

    public virtual void Add(TEntity entity)
    {
        entity.ObjectState = ObjectState.Added;
        _dbSet.Attach(entity);
        _context.SyncObjectState(entity);
    }

9) UnitOfWork SaveChanges(): 9)UnitOfWork SaveChanges():

    public int SaveChanges()
    {
        return _dataContext.SaveChanges();
    }

When I step through this, the entity that is attached in #8 looks right, the status is added, when save changes is called there are no changes recorded in the base SaveChanges(): 当我逐步执行此操作时,#8中附加的实体看起来正确,状态被添加,当调用保存更改时,基本SaveChanges()中没有记录更改:

    public override int SaveChanges()
    {
        SyncObjectsStatePreCommit();
        base.ChangeTracker.DetectChanges();
        var result = base.ChangeTracker.HasChanges();
        var changes = base.SaveChanges();

result here is false 结果在这里是假的

How could that be? 怎么可能 What am I missing, other than abstraction hell and UoW/Repos on top of EF6 context, (I inherited this). 除了EF6上下文之上的抽象地狱和UoW / Repos,我还缺少什么(我继承了这一点)。

Someone out there see anything ELSE dumb? 外面有人看不见其他东西吗?

No errors, no exceptions, the inserts just don't happen. 没有错误,没有异常,插入只是不会发生。

Problem solved, the issue was that Ninject was injecting a different context into our repositories. 问题解决了,问题在于Ninject向我们的存储库注入了不同的上下文。

We found two ways to solve this: 我们找到了两种解决方法:

Option 1) In our unit of work we created an additional constructor that just receives a dbcontext and creates new repositories passing them the dbcontext. 选项1)在我们的工作单元中,我们创建了一个附加的构造函数,该构造函数仅接收dbcontext并创建将其传递给dbcontext的新存储库。 We left the old constructor as well so we can inject the repos for testing. 我们也离开了旧的构造函数,因此我们可以注入存储库进行测试。 This worked great and was a trivial change to make. 这非常有效,并且是一个微不足道的改变。

Option 2) Removed Ninject and overloaded the constructors for each Controller, manually creating the items. 选项2)删除了Ninject,并为每个Controller重载了构造函数,从而手动创建了项。 Since this project was small, we only had to touch 6 controllers and it worked fine, just 4 new lines of code in each. 由于该项目很小,我们只需要触摸6个控制器,它就可以正常工作,每个控制器中只有4行新代码。 We left the old constructor as well so we can inject the services for testing. 我们也离开了旧的构造函数,因此可以注入服务进行测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM