1
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;
5
  • 1
    Are you sure you posted the right code? In your service you do _repository.Users.Attach(user); while your SqlRepository doesn't even have a DbSet<User>. Also, the Update method of your service expects a User object, and you're passing in message.Text = "Test". Does that even compile? :-) Commented Feb 24, 2011 at 9:24
  • 1
    yup, this code makes no sense. even if it did (compile that is), the code would not persist to the db. all your doing is attaching to the context, AFTER you've made a change. you either need to set the EntityState to modified, or attach to the context and THEN modify the entity. Commented Feb 24, 2011 at 11:26
  • @kristof-claes @rpm1984 Sry guy, code is now editet. Commented Feb 24, 2011 at 11:50
  • Does you MessageServices and the class (Controller) where the method Edit is defined use the same Repository/SqlDataContext instance? Commented Feb 24, 2011 at 13:09
  • @jehof they are injected, i edit me post for details. Commented Feb 24, 2011 at 13:30

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

@JinDave: I have an Edit appended to the answer for a small test if the DataContexts are identical.
OK, so it's an EF problem. Did you take a close look at RPM1984's comment to your question? I don't believe that _repository.Messages.First... attaches to the context. You either need to call Attach directly after you fetch the object from the DB and BEFORE you change the object, or you could use the Find method of DbSet to pick the object by ID. (You could extend the repository for this purpose. I think Find attaches automatically to the context, not sure though.) Otherwise you have to change the Entity state to Modified manually, as you already tried.
i know that this is an old question but if feel it needs to be pointed out. Lets say you have 2 repositories, both gets an context in their constructor, with the current code "For<SqlDataContext>().Use(() => new SqlDataContext());" 2 new contexts would be created, but if you use "For<SqlDataContext>().HybridHttpOrThreadLocalScoped.Use(() => new SqlDataContext());" both repositories would share the same context.
@Joakim: Hm, interesting! According to the "ReferenceEquals" check in my answer the contexts in the Repository and the Service are the same, as JinDave stated in his comment above and also in the edited question. If you are right, this is impossible and the contexts should be different. Strange thing...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.