Using Simple Injector with Unit Of Work & Repository Pattern in Windows Form

c# entity-framework inversion-of-control repository-pattern simple-injector

Question

In my Windows form application, I'm attempting to incorporate IoC. I decided on Simple Injector due of its speed and portability. In my apps, I also use the unit of work and repository patterns. Here is the organization:

DbContext:

public class MemberContext : DbContext
{
    public MemberContext()
        : base("Name=MemberContext")
    { }

    public DbSet<Member> Members { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();\
    }
}

Model:

public class Member
{
    public int MemberID { get; set; }
    public string Name { get; set; }
}

GenericRepository:

public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity> 
    where TEntity : class
{
    internal DbContext context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(DbContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }
}

MemberRepository:

public class MemberRepository : GenericRepository<Member>, IMemberRepository
{
    public MemberRepository(DbContext context)
        : base(context)
    { }
}

UnitOfWork:

public class UnitOfWork : IUnitOfWork
{
    public DbContext context;

    public UnitOfWork(DbContext context)
    {
        this.context = context;
    }

    public void SaveChanges()
    {
        context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }

        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

MemberService:

public class MemberService : IMemberService
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IMemberRepository memberRepository;

    public MemberService(IUnitOfWork unitOfWork, IMemberRepository memberRepository)
    {
        this.unitOfWork = unitOfWork;
        this.memberRepository = memberRepository;
    }

    public void Save(Member member)
    {
        Save(new List<Member> { member });
    }

    public void Save(List<Member> members)
    {
        members.ForEach(m =>
            {
                if (m.MemberID == default(int))
                {
                    memberRepository.Insert(m);
                }
            });
        unitOfWork.SaveChanges();
    }
}

I just provide a textbox for member names and a button to save the form to the database in the member form. The code in member form is as follows:

frmMember:

public partial class frmMember : Form
{
    private readonly IMemberService memberService;

    public frmMember(IMemberService memberService)
    {
        InitializeComponent();

        this.memberService = memberService;
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        Member member = new Member();
        member.Name = txtName.Text;
        memberService.Save(member);
    }
}

In Program.cs, I implement the SimpleInjector (see http://simpleinjector.readthedocs.org/en/latest/windowsformsintegration.html), as can be seen in the code below:

static class Program
{
    private static Container container;

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Bootstrap();
        Application.Run(new frmMember((MemberService)container.GetInstance(typeof(IMemberService))));
    }

    private static void Bootstrap()
    {
        container = new Container();

        container.RegisterSingle<IMemberRepository, MemberRepository>();
        container.Register<IMemberService, MemberService>();
        container.Register<DbContext, MemberContext>();
        container.Register<IUnitOfWork, UnitOfWork>();

        container.Verify();
    }
}

The software does not save to the database when I run it and add a member. If I alteredcontainer.Register to container.RegisterSingle , it will save to database. According to the paperwork,RegisterSingle is going to make my class a Singleton. RegisterLifeTimeScope will produce an error, thus I can't use it.

"An exception was thrown by the registered delegate for type IMemberService. Although the instance is requested outside of a Lifetime Scope, the IUnitOfWork is registered as having a "Lifetime Scope" lifestyle."

1) How can I use the UnitOfWork & Repository pattern with SimpleInjector in a Windows Form?
2) Am I correctly using the patterns?

1
10
4/22/2015 3:58:59 PM

Accepted Answer

The disparity in lifestyles between your service, repository, unitofwork, and dbcontext is the difficulty you face.

Due to theMemberRepository has a Singleton lifestyle, Simple Injector will build a single instance that will be utilized throughout the program's lifespan, which with a WinForms application might be days, weeks, or even months. The immediate result of registering theMemberRepository regardless of the lifestyle chosen during registration, this class will become Singleton and all of its dependencies will follow suit. This issue is referred to as Dependency in Captivity.

On a related note: The Simple Injector's diagnostic assistance can detect this configuration error and will display or throw a Warning about Potential Lifestyle Mismatch.

So theMemberRepository is Singleton and has one and the sameDbContext throughout the duration of the application. even so,UnitOfWork which is dependent on as wellDbContext will obtain an other instance of theDbContext due to the fact that registration forDbContext is Temporary. In your case, this context won't ever be able to store the freshly formedMember due to thisDbContext possesses none of the newly generatedMember the member is made in a distinct manner.DbContext .

When you alter your vehicle's registrationDbContext to RegisterSingleton it will begin to function because every service, class, or other entity now depends onDbContext receives the exact same instance.

But having one means that this is unquestionably the answer.DbContext for the duration of the application, as you are surely already aware, will get you into trouble. This post goes into great detail to explain this.

You can achieve your goal by utilizing a Scoped instance of theDbContext which you've already attempted. You are missing certain instructions on how to use Simple Injector's lifetime scope functionality (and most of the other containers out there). The exception message makes it obvious that there must be an active scope when utilizing a Scoped lifestyle. It's not difficult to begin a lifelong scope:

using (ThreadScopedLifestyle.BeginScope(container)) 
{
    // all instances resolved within this scope
    // with a ThreadScopedLifestyleLifestyle
    // will be the same instance
}

You can read here in detail.

The registrations were changed to:

var container = new Container();
container.Options.DefaultScopedLifestyle = new ThreadScopedLifestyle();

container.Register<IMemberRepository, MemberRepository>(Lifestyle.Scoped);
container.Register<IMemberService, MemberService>(Lifestyle.Scoped);
container.Register<DbContext, MemberContext>(Lifestyle.Scoped);
container.Register<IUnitOfWork, UnitOfWork>(Lifestyle.Scoped);

while converting the code frombtnSaveClick() to:

private void btnSave_Click(object sender, EventArgs e)
{
    Member member = new Member();
    member.Name = txtName.Text;

    using (ThreadScopedLifestyle.BeginScope(container)) 
    {
        var memberService = container.GetInstance<IMemberService>();
        memberService.Save(member);
    }
}

is essentially what you require.

But We now have a brand-new issue. To obtain a Scoped instance of the Anti-pattern Service Locator, we are now using it.IMemberService implementation. In order to handle this for us as a Integrated Concern in the application, we therefore need some infrastructure. A Decorator is the ideal approach to put this into practice. Also see here. This will appear as:

public class ThreadScopedMemberServiceDecorator : IMemberService
{
    private readonly Func<IMemberService> decorateeFactory;
    private readonly Container container;

    public ThreadScopedMemberServiceDecorator(Func<IMemberService> decorateeFactory,
        Container container)
    {
        this.decorateeFactory = decorateeFactory;
        this.container = container;
    }

    public void Save(List<Member> members)
    {
        using (ThreadScopedLifestyle.BeginScope(container)) 
        {
            IMemberService service = this.decorateeFactory.Invoke();

            service.Save(members);
        }
    }
}

You now register this in the Simple Injector as a (Singleton) Decorator.Container as in this:

container.RegisterDecorator(
    typeof(IMemberService), 
    typeof(ThreadScopedMemberServiceDecorator),
    Lifestyle.Singleton);

Depending on the container, a class will be provided.IMemberService using thisThreadScopedMemberServiceDecorator . The container in this will inject aFunc<IMemberService> which, when called, returns a container instance with the specified lifestyle.

The problem from your example will be resolved by adding this Decorator (and its registration) and altering the lifestyles.

However, I anticipate that your application will ultimately have anIMemberService , IUserService , ICustomerService , etc... Therefore, you require a decorator for every singleIXXXService not at all DRY in my opinion. assuming that all services implementSave(List<T> items) You might think about developing an open generic interface:

public interface IService<T>
{
    void Save(List<T> items); 
}

public class MemberService : IService<Member>
{
     // same code as before
}

Using Batch-Registration, you register all implementations on a single line:

container.Register(typeof(IService<>),
    new[] { Assembly.GetExecutingAssembly() },
    Lifestyle.Scoped);

Furthermore, you can incorporate each of these instances into a single open generic implementation of the aforementionedThreadScopedServiceDecorator .

The pattern command/handler (you should really read the link!) would, in my opinion, be even better for this kind of job. In a nutshell: Every case study in this pattern is converted to a message object (a command) that is handled by a single command handler, which can be embellished with, for example, aSaveChangesCommandHandlerDecorator and aThreadScopedCommandHandlerDecorator and LoggingDecorator so forth.

The following would thus be your example:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public class CreateMemberCommand
{
    public string MemberName { get; set; }
}

using the subsequent handlers:

public class CreateMemberCommandHandler : ICommandHandler<CreateMemberCommand>
{
    //notice that the need for MemberRepository is zero IMO
    private readonly IGenericRepository<Member> memberRepository;

    public CreateMemberCommandHandler(IGenericRepository<Member> memberRepository)
    {
        this.memberRepository = memberRepository;
    }

    public void Handle(CreateMemberCommand command)
    {
        var member = new Member { Name = command.MemberName };
        this.memberRepository.Insert(member);
    }
}

public class SaveChangesCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> decoratee;
    private DbContext db;

    public SaveChangesCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratee, DbContext db)
    {
        this.decoratee = decoratee;
        this.db = db;
    }

    public void Handle(TCommand command)
    {
        this.decoratee.Handle(command);
        this.db.SaveChanges();
    }
}

And it can now vary depending onICommandHandler<T> :

public partial class frmMember : Form
{
    private readonly ICommandHandler<CreateMemberCommand> commandHandler;

    public frmMember(ICommandHandler<CreateMemberCommand> commandHandler)
    {
        InitializeComponent();
        this.commandHandler = commandHandler;
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        this.commandHandler.Handle(
            new CreateMemberCommand { MemberName = txtName.Text });
    }
}

All of this can be recorded as follows:

container.Register(typeof(IGenericRepository<>), 
    typeof(GenericRepository<>));
container.Register(typeof(ICommandHandler<>), 
    new[] { Assembly.GetExecutingAssembly() });

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(SaveChangesCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
    typeof(ThreadScopedCommandHandlerDecorator<>),
    Lifestyle.Singleton);

This layout will negate the requirement forUnitOfWork and a (certain) service entirely.

20
11/9/2017 9:45:25 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow