1

I re-posted this question as I think it is a bit vague. New Post

I am currently using a Windows Service that is on a 2 minute timer. I am using EF code first with a repository pattern for data access. I am using Ninject to inject my dependencies. I have the following bindings in my NinjectDependencyResolver class:

ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["Database"];

Bind<IDatabaseFactory>().To<DatabaseFactory>()
                        .InSingletonScope()
                        .WithConstructorArgument("connectionString", connectionStringSettings.Name);

Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
Bind<IMyRepository>().To<MyRepository>().InSingletonScope();

When my service runs every 2 minutes I do some thing similar to this:

foreach (var row in rows)
{
    var existing = myRepository.GetById(row.Id);

    if (existing == null)
    {
        existing = new Row();

        myRepository.Add(existing);

        unitOfWork.Commit();
    }
}

I am starting to see an error in my logs that say:

The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.

Is it correct to use InSingeltonScope when using Ninject in a Windows Service? I believe I tried using different scopes like InTransientScope but I could only get InSingeltonScope to work with data access. Does the error message have anything to do with Scope or is it unrelated?

Community
  • 1
  • 1
Thomas
  • 5,618
  • 6
  • 40
  • 79

2 Answers2

3

Assuming that the service is not the only process that operates on the database you shouldn't use Singleton. What happens in this case is that you are reusing a DBContext that has cached entities which are out of date.

The better way is to treat each timer execution of the service in a similar way like it is a web/wcf request and create a new job processor for the request.

var processor = factory.CreateRowsProcessor();
processor.ProcessRows(rows);

public class RowsProcessor
{
    public Processor(UoW uow, ....)
    {
        ...
    }

    public void ProcessRows(Rows[] rows)
    {
        foreach (var row in rows)
        {
            var existing = myRepository.GetById(row.Id);

            if (existing == null)
            {
                existing = new Row();
                myRepository.Add(existing);

                unitOfWork.Commit();
            }
        }
    }
}

Depending of the problem it might even better to put the loop outside and have a new processor for each single row.

Read http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/ for more information about factories. Also have a look at the InCallScope of the named scope extension if you need to inject the UoW into multiple classes. http://www.planetgeek.ch/2010/12/08/how-to-use-the-additional-ninject-scopes-of-namedscope/

Remo Gloor
  • 32,609
  • 4
  • 66
  • 97
  • Remo, I read your article about the ninject factory but it seems that this is part of Ninject 3.0? I am currently using 2.2.1.4. Are there any other articles I can look at? I seem to be in over my head when I chose to use Ninject with a windows service. Any help is appreciated. – Thomas Feb 09 '12 at 04:07
  • I recommend updating. It should be possible without any huge changes. Otherwise you have to implement the factory manually but anything else stays the same. – Remo Gloor Feb 09 '12 at 11:08
  • I will look into Ninject 3 as an option. Thanks. – Thomas Feb 09 '12 at 17:50
2

InSingletonScope will create singleton context = one context for the whole lifetime of your service. It is very bad solution. Because context holds all objects from all previous time events its memory consumption grows and there are possibilities to get errors as the one you are receiving at the moment (but the error really can be unrelated to your singleton context but most likely it is not). The exception says that you have two different objects with the same key identifier tracked by the context - that is not allowed.

Instead of using singleton uow, repository and context use singleton factory and in each time even request new fresh instances from the factory. Dispose context at the end of the time event processing.

Community
  • 1
  • 1
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • Ladislav, can you point to some code samples? I am a bit lost on how to setup a singleton factory with bindings in such a way that new instances of the objects will be created every time the factory is used. Any help is much appreciated. – Thomas Feb 09 '12 at 04:00
  • Factory will simply call constructors of your objects. Not everything has to be injected. Point of factory is to know how to create instance of particular type. – Ladislav Mrnka Feb 09 '12 at 13:11
  • Ladislav, I reposted this question with a clearer description of what I am trying to do. http://stackoverflow.com/questions/9216006/ninject-scope-problems-with-topshelf-ninject-and-ef-code-first – Thomas Feb 09 '12 at 17:49