【问题标题】:Update object in Entity Framework更新实体框架中的对象
【发布时间】:2011-02-24 09:15:27
【问题描述】:
public class SqlDataContext : DbContext
    {
        public DbSet<Message> Messages { get; set; }

        protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
        {

            base.OnModelCreating(modelBuilder);
        }
    }

和存储库类:

 public class SqlRepository : IRepository
        {
            private SqlDataContext _dataContext;
            public SqlRepository(SqlDataContext dataContext) {
                _dataContext = dataContext;
            }

            public DbSet<Message> Messages {
                get { return _dataContext.Messages; } 
            }

            public void Commit() {
                _dataContext.SaveChanges();
            }

        }

和服务等级:

public class MessagesServices : IMessagesServices
    {
        #region Repository        
        private IRepository _repository;
        public UsersServices(IRepository repository) {
            _repository = repository;
        }
        #endregion


        /// <summary>
    /// Update the Message in Repository
    /// </summary>
    public void Update(Message message) {
        if (message != null) {
            _repository.Messages.Attach(message);
            _repository.Commit();
        }
    }
}

如果我收到这样的消息:

public ViewResult Edit(int messageId)
    {
        var message = _repository.Messages.First(j => j.MessageId == messageId);
        message.Text = "Test";
        _userService.Update(message);
    }

一切都会好起来的,但更改不会保存到数据库中。

消息控制器构造函数

private IRepository _repository;
        private IMessagesServices _messageServices;
        public MessagesController(IRepository repository, IMessagesServices messageServices)
        {
            _repository = repository;
            _messageServices = messageServices;
        }

存储库、服务和数据上下文通过结构图注入:

/// <summary>
        /// Configuration registry modules for DI
        /// </summary>
        public class WebUiRegistry : Registry
        {
            public WebUiRegistry()
            {
                For<DbContext>().Use(() => new SqlDataContext());      
                For<IRepository>().HttpContextScoped().Use<SqlRepository>();
                For<IUsersServices>().Use<UsersServices>();
                For<IMessagesServices>().Use<MessagesServices>();
                For<IJobAdvertsServices>().Use<JobAdvertsServices>();
                For<ILog>().Use<SqlLogServices>();
            }
        }

@slauma 你说得对。

如果我试试这个:

ObjectFactory.GetInstance<SqlDataContext>().Messages.Attach(message);
                ObjectFactory.GetInstance<SqlDataContext>().Entry(message).State = EntityState.Modified;
                ObjectFactory.GetInstance<SqlDataContext>().SaveChanges();

它们都工作正常,所以我有多个 SqlDataContext 实例。

但是这个:

For<SqlDataContext>().Use(() => new SqlDataContext());

不要帮忙。有什么想法吗?

我想,问题就在这里

private IRepository _repository;
        private IMessagesServices _messageServices;
        public MessagesController(IRepository repository, IMessagesServices messageServices)
        {
            _repository = repository;
            _messageServices = messageServices;
        }

因为在这里注入了存储库(在存储库内部注入了 SqlDataContext)并且同时注入了服务(在注入的存储库中,在存储库 SqlDataContext 中)

public class MessageController : Controller
    {
        private IRepository _repository;
        private IMessagesServices _messagesServices;
        public MessageController(IRepository repository, IMessagesServices messagesServices)
        {
            _repository = repository;
            _messagesServices = messagesServices;

            bool sameDataContexts = object.ReferenceEquals(((SqlRepository)_repository)._dataContext, ((SqlRepository)((MessagesServices)_messagesServices)._repository)._dataContext);

        }

结果为真。

可行的解决方案:

private IRepository _repository;
        private IMessagesServices _messageServices;
        public MessagesController(IRepository repository)
        {
            _repository = repository;
            _messageServices = ObjectFactory.GetInstance<IUsersServices>();
        }

在服务类中:

/// <summary>
    /// Update the Message in Repository
    /// </summary>
    public void Update(Message message) {
        if (message != null) {
            _repository.Messages.Attach(message);
            ObjectFactory.GetInstance<SqlDataContext>().Entry(message).State = EntityState.Modified;
            _repository.Commit();
        }

但是我在这里有两个构造函数参数的解决方案吗?并且不使用

ObjectFactory.GetInstance<SqlDataContext>().Entry(message).State = EntityState.Modified;

【问题讨论】:

  • 您确定您发布了正确的代码吗?在您的服务中,您执行_repository.Users.Attach(user);,而您的SqlRepository 甚至没有DbSet&lt;User&gt;。此外,您的服务的 Update 方法需要一个 User 对象,并且您正在传递 message.Text = "Test"。这甚至可以编译吗? :-)
  • 是的,这段代码没有意义。即使它这样做了(即编译),代码也不会持久保存到数据库中。在你做出改变之后,你所做的一切都是依附于上下文。您要么需要将EntityState 设置为已修改,要么附加到上下文然后修改实体。
  • @kristof-claes @rpm1984 不好意思,代码现在正在编辑中。
  • 你 MessageServices 和定义方法 Edit 的类(控制器)使用相同的 Repository/SqlDataContext 实例吗?
  • @jehof 他们被注射了,我编辑我的帖子了解详情。

标签: asp.net-mvc entity-framework entity-framework-4 asp.net-mvc-3


【解决方案1】:

尝试替换此行...

For<DbContext>().Use(() => new SqlDataContext());

...通过

For<SqlDataContext>().Use(() => new SqlDataContext());

(如果您还需要在其他地方解析DbContext,则添加此行)

您的SqlRepository 想要将SqlDataContext 作为构造函数参数,而不是DbContext。因此,当IRepository 得到解析和IMessagesServices 得到解析时,您的 DI 容器可能会创建两个新的 SqlDataContext 实例,而不是使用已注册的 SqlDataContext 实例。然后,您的存储库和您的服务类将使用两种不同的数据上下文,这将导致更新失败。

这只是一个猜测,我不知道StructureMap的细节。但是对于 Unity(我更熟悉),这将不起作用:Unity 不会在需要派生类的地方注入已注册基类的实例。您需要注册要注入的确切类类型。

编辑: 最好检查一下注入到 Repository 和 Service 中的 DataContexts 是否真的相同,例如在 Controller 的 Edit 方法中:

bool sameDataContexts = object.ReferenceEquals(_repository._dataContext,
    _messageServices._repository._dataContext);

(在 Repository 中创建 _dataContext 并在 Service 类 public 中创建 _repository 临时用于该测试。)

这样我们可以清楚地区分您是否有注入问题或 EF 问题。

【讨论】:

  • @JinDave:如果 DataContexts 相同,我在答案后面附加了一个编辑。
  • 好的,这是一个 EF 问题。您是否仔细查看了 RPM1984 对您的问题的评论?我不相信_repository.Messages.First... 附加到上下文中。您需要在从数据库中获取对象之后并在更改对象之前直接调用Attach,或者您可以使用DbSetFind 方法按ID 选择对象。 (您可以为此目的扩展存储库。我认为 Find 会自动附加到上下文中,但不确定。)否则您必须手动将实体状态更改为已修改,就像您已经尝试过的那样。
  • 我知道这是一个老问题,但如果觉得需要指出。假设您有 2 个存储库,它们都在其构造函数中获取上下文,当前代码为“For().Use(() => new SqlDataContext());”将创建 2 个新上下文,但如果您使用“For().HybridHttpOrThreadLocalScoped.Use(() => new SqlDataContext());”两个存储库将共享相同的上下文。
  • @Joakim:嗯,有趣!根据我的回答中的“ReferenceEquals”检查,存储库和服务中的上下文是相同的,正如 JinDave 在上面的评论和编辑的问题中所说的那样。如果你是对的,这是不可能的,而且上下文应该不同。奇怪的东西……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-19
相关资源
最近更新 更多