David Horák
David Horák

Reputation: 5565

Update object in Entity Framework

public class SqlDataContext : DbContext
    {
        public DbSet<Message> Messages { get; set; }

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

            base.OnModelCreating(modelBuilder);
        }
    }

and repository class:

 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();
            }

        }

and service class:

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();
        }
    }
}

If i get any message like this:

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

Everything will be okay, but the changes are not saved to the database.

Message Controller constructor

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

repository, service and data context are injected by stucture map:

/// <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 You have right.

If i try this:

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

Them all works fine, so i have multiple instance of SqlDataContext.

But this:

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

don't help. Any idea?

I think, problem is here

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

Because repository is here injected (inside repository is injected SqlDataContext) and in same moment is injected services (inside injected repository, in repository 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);

        }

result is TRUE.

WORKING Solution:

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

and in services class:

/// <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();
        }

But i here any solution for two constructor parameters ? And non-using

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

Upvotes: 1

Views: 1462

Answers (1)

Slauma
Slauma

Reputation: 177173

Try to replace this line ...

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

...by

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

(or add this line if you also need DbContext to be resolved somewhere else)

Your SqlRepository wants to have a SqlDataContext as constructor parameter, not a DbContext. So it could be the case the your DI container creates two new instances of the SqlDataContext when IRepository gets resolved and when IMessagesServices gets resolved, instead of using the registered SqlDataContext instance. Your repository and your service class would work then with two different data contexts which would result in the failing update.

It's only a guess, I don't know the details of StructureMap. But with Unity (I am more familiar with), this would not work: Unity doesn't inject instances of a registered base class at places where a derived class is required. You need to register for the exact class type you want to inject.

Edit: It would be a good idea to check if the DataContexts injected into the Repository and into the Service are really the same, for instance in your Edit method of the Controller:

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

(Make _dataContext in the Repository and _repository in the Service class public temporarily for that test.)

This way we can clearly distinguish if you have an injection problem or an EF problem.

Upvotes: 1

Related Questions