【问题标题】:SaveChanges() and adding to DB not workingSaveChanges() 并添加到数据库不起作用
【发布时间】:2019-04-08 15:03:27
【问题描述】:

我正在努力将实体框架添加到我们的 Web 应用程序 asp.net MVC 5,但我很难保存更改并将其添加到数据库中。我使用通用的 BaseRepository 设置了 UnitOfWork,并且我尝试了一些尝试使其正常工作的方法。首先,我想我可以像这样使用 AutoFac 在 UnitOfWork 中注入我的 repo

public UnitOfWork(IServiceItem serviceItem
        , ITechServiceItem techServiceItem
        , ITechnicianTime technicianTime
        , ISproc sproc
        , IRepairOrder repairOrder
        , ICustomer customer
         , IRepairOrderStatus repairOrderStatus
        , IRepairOrderUnit repairOrderUnit
        , IFiles files
        , IPartInventory partInventory
        , IRepairOrderItems repairOrderItems
        )
        {
            RepairOrderItems = repairOrderItems;
            PartInventory = partInventory;
            Files = files;
            RepairOrderUnit = repairOrderUnit;
            RepairOrderStatus = repairOrderStatus;
            RepairOrder = repairOrder;
            Customer = customer;
            Sproc = sproc;
            ServiceItem = serviceItem;
            TechServiceItem = techServiceItem;
            TechnicianTime = technicianTime;
        }

我的 BaseRepo 就像

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected DataDbContext _db;

       public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected DataDbContext _db;

        internal void GetData()
        {
            if (_db == null)
            {
                string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
                var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
                _db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
            }
        }

        public TEntity Get(int id)
        {
            return _db.Set<TEntity>().Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return _db.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return _db.Set<TEntity>().Where(predicate);
        }

        public void Add(TEntity entity)
        {
            _db.Set<TEntity>().Add(entity);
        }

        public void AddRange(IEnumerable<TEntity> entities)
        {
            _db.Set<TEntity>().AddRange(entities);
        }

        public void Remove(TEntity entity)
        {
            _db.Set<TEntity>().Remove(entity);
        }

        public void RemoveRange(IEnumerable<TEntity> entities)
        {
            _db.Set<TEntity>().RemoveRange(entities);
        }

        public int CompleteData()
        {
            return _db.SaveChanges();
        }

        public TEntity Get(int id)
        {
            return _db.Set<TEntity>().Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return _db.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return _db.Set<TEntity>().Where(predicate);
        }

        public void Add(TEntity entity)
        {
            _db.Set<TEntity>().Add(entity);
        }

        public void AddRange(IEnumerable<TEntity> entities)
        {
            _db.Set<TEntity>().AddRange(entities);
        }

        public void Remove(TEntity entity)
        {
            _db.Set<TEntity>().Remove(entity);
        }

        public void RemoveRange(IEnumerable<TEntity> entities)
        {
            _db.Set<TEntity>().RemoveRange(entities);
        }

        public int CompleteData()
        {
            return _db.SaveChanges();
        }
}

还有我的 StartUp.Configuration

 public void Configuration(IAppBuilder app)
        {
            var builder = new ContainerBuilder();
            HttpConfiguration config = GlobalConfiguration.Configuration;

            // REGISTER DEPENDENCIES
            builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();
            builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
            builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
            builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).InstancePerRequest();
            builder.Register(c => HttpContext.Current.User).InstancePerRequest();
            builder.Register(c => app.GetDataProtectionProvider()).InstancePerRequest();

            builder.RegisterType<ApplicationUserStore>().As<IUserStore<EverLogicMamber, int>>()
                  .WithParameter(new TypedParameter(typeof(ISecurityOfWork), new SecurityOfWork(new SecurityDbContext())))
                .InstancePerRequest();

            //Database
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
            builder.RegisterType<SecurityOfWork>().As<ISecurityOfWork>().InstancePerRequest();
            //Service
            builder.RegisterType<TechnicianTimeService>().As<ITechnicianTimeService>().InstancePerRequest();
            builder.RegisterType<PartService>().As<IPartService>().InstancePerRequest();
            builder.RegisterType<TechServiceItemService>().As<ITechServiceItemService>().InstancePerRequest();
            //Repo
            builder.RegisterType<Company>().As<ICompany>().InstancePerRequest();
            builder.RegisterType<Views>().As<IViews>().InstancePerRequest();

            builder.RegisterType<RepairOrderItems>().As<IRepairOrderItems>().InstancePerRequest();
            builder.RegisterType<PartInventory>().As<IPartInventory>().InstancePerRequest();
            builder.RegisterType<Files>().As<IFiles>().InstancePerRequest();
            builder.RegisterType<TechDashboardService>().As<ITechDashboardService>().InstancePerRequest();
            builder.RegisterType<RepairOrderUnit>().As<IRepairOrderUnit>().InstancePerRequest();
            builder.RegisterType<RepairOrderStatus>().As<IRepairOrderStatus>().InstancePerRequest();
            builder.RegisterType<Customer>().As<ICustomer>().InstancePerRequest();
            builder.RegisterType<ServiceItem>().As<IServiceItem>().InstancePerRequest();
            builder.RegisterType<RepairOrder>().As<IRepairOrder>().InstancePerRequest();
            builder.RegisterType<Sproc>().As<ISproc>().InstancePerRequest();
            builder.RegisterType<TechServiceItem>().As<ITechServiceItem>().InstancePerRequest();
            builder.RegisterType<TechnicianTime>().As<ITechnicianTime>().InstancePerRequest();

            // REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
            builder.RegisterControllers(typeof(MvcApplication).Assembly);
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            builder.RegisterWebApiFilterProvider(config);
            builder.RegisterWebApiModelBinderProvider();

            var container = builder.Build();

            // REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

            app.UseAutofacMiddleware(container);
            app.UseAutofacMvc();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            ConfigureAuth(app);
        }

但是通过这种设置,数据库不会更新或添加新实体。 然后我尝试从 UnitOfWork 中删除 Dependcy 注入并将 UnitOfWork 设置为

 protected DataDbContext _db;
      public UnitOfWork(DataDbContext context)
        {
            GetData();
            RepairOrderItems = new RepairOrderItems(_db);
            PartInventory = new PartInventory(_db);
            Files = new Files(_db);
            RepairOrderUnit = new RepairOrderUnit(_db);
            RepairOrderStatus = new RepairOrderStatus(_db);
            RepairOrder = new RepairOrder(_db);
            Customer = new Customer(_db);
            Sproc = new Sproc(_db);
            ServiceItem = new ServiceItem(_db);
            TechServiceItem = new TechServiceItem(_db);
            TechnicianTime = new TechnicianTime(_db);
        }
  internal void GetData()
        {
            if (_db == null)
            {
                string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
                var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
                _db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
            }
        }

并将 SaveChanges 从 BaseRepo 移动到 UnitOfWork,但仍然没有保存或添加到数据库中。 我错过了什么????

【问题讨论】:

  • 当您拨打SaveChanges 时,会发生什么?有任何错误信息吗?接下来,在调用这个方法之前,_db.ChangeTrackerHasChanges()Entries的状态是什么?
  • 也许您可以开始而不将 DbContext 包装在额外的工作单元和存储库抽象中。

标签: asp.net-mvc entity-framework autofac


【解决方案1】:

TL;DR 问题是您的所有存储库都使用单独的、独立的 DbContexts,因此当您调用 SaveChanges 时,注入到您的 UnitOfWork 中的 DbContext 没有待处理的更改在它上面,所以这就是为什么你没有看到数据库有任何变化。

为了使工作单元正常运行,您的UnitOfWork 类以及您的代码需要执行数据持久性的所有存储库类都必须共享相同的DbContext 实例。在您的代码中,很明显每个存储库都有一个工厂方法来创建它自己的独立 DbContext 实例。

  • 从您的BaseRepository 类中删除GetData() 工厂方法,而是要求您的EverLogicDbContext 实例的实例通过AutoFac 注入BaseRepository 的构造函数。这将要求您的所有 Repository 子类也需要有一个接受相同 EverLogicDbContext 的构造函数。
  • 根据您上次的编辑,UnitOfWork 类必须接受存储库使用的相同、共享的EverLogicDbContext。由于您已使用 asp.net-mvc 进行标记,因此 RequestPerInstance 生命周期范围适用于您的方案。
  • 您的UnitOfWork 类需要控制SaveChanges(Async) 方法,因此请从BaseRepository 类中删除CompleteData 方法。
  • 您似乎已经这样做了,DbContext 需要注册InstancePerRequest

builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();

如果所有这些都正确地联系在一起:

  • AutoFac 将在处理每个请求期间第一次需要它时创建具体的 DbContext 实例。
  • 然后,所有存储库将在请求的生命周期内共享相同的 DbContext 实例,并且 DbContext 将跟踪您的服务所做的临时更改。
  • UnitOfWork 注入到您的主要“业务逻辑”(例如控制器,或 Orchestrator / Handler)然后将能够通过简单地在共享的 DbContext 上调用 SaveChangesAsync 来执行 Commit 所采取的操作。这一切都将在单个数据库连接下发生,轻量级事务也是如此。

与上述其他 cmets 一样,IMO 实体框架已经是一个具有内置事务支持的高级框架,因此如果所有 ACID 活动都将针对同一个数据库进行,那么过度设计“UnitOfWork”模式就没有什么意义了(并且可以包装到同一个 DbContext 中)。

【讨论】:

  • 好吧,我没有使用同一个数据库。每个用户都隶属于一家公司,每家公司都有自己的数据库,还有一个额外的数据库来跟踪与公司无关的数据和登录信息。这就是我让 GetData() 创建连接字符串的原因,因为我需要知道我要连接到哪个数据库
  • 我想表明你的存储库跨越多个数据库会更清楚(你提到adding to the database)对于多个数据库,相同的原则适用,IoC 每个请求为每个数据库创建一个实例,你'将需要将所有 shared DbContexts(在存储库中使用)注入到您的 UnitOfWork 类中。您还需要跟踪每个 DbContext 是否有待处理的更改(请参阅:stackoverflow.com/a/26257585)。但要真正实现事务性,您需要使用 TransactionScope 之类的东西并在您的数据库上启用 DTC 或 XA。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
  • 1970-01-01
  • 1970-01-01
  • 2014-04-22
  • 1970-01-01
相关资源
最近更新 更多