Using Simple Injector with Unit Of Work & Repository Pattern in Windows Form
我正试图在我的Windows窗体应用程序中实现IOC。我的选择落在了简单的注射器上,因为它既快又轻。我也在我的应用程序中实现了工作单元和存储库模式。结构如下:
DbContext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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>();\ } } |
模型:
1 2 3 4 5 | public class Member { public int MemberID { get; set; } public string Name { get; set; } } |
通用存储库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } } |
成员存储库:
1 2 3 4 5 6 | public class MemberRepository : GenericRepository<Member>, IMemberRepository { public MemberRepository(DbContext context) : base(context) { } } |
UnitOfWork:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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(); } } |
在成员窗体中,我只添加一个文本框来输入成员名称,并添加一个按钮保存到数据库。这是成员形式的代码:
会员:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); } } |
我在program.cs中实现simpleinjector(请参阅http://simpleinjector.readthedocs.org/en/latest/windowsformsintegration.html),如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(); } } |
当我运行程序并添加成员时,它不会保存到数据库中。如果我把
"为IMemberService类型注册的委托引发了异常。iUnitOfWork注册为"终身范围"生活方式,但实例是在终身范围之外请求的。"
1)如何将Windows窗体中的SimpleInjector与UnitOfWork&Repository模式一起使用?< BR>2)我是否正确地实现模式?
您的问题在于服务、存储库、UnitOfWork和DbContext之间的生活方式不同。好的。
由于
附带说明:简单注射器的诊断服务能够发现这种配置错误,并显示/抛出潜在的生活方式不匹配警告。好的。
因此,
当您将
但这肯定不是解决方案,因为在应用程序的生命周期中使用一个
您需要的解决方案是使用您已经尝试过的
1 2 3 4 5 6 | using (ThreadScopedLifestyle.BeginScope(container)) { // all instances resolved within this scope // with a ThreadScopedLifestyleLifestyle // will be the same instance } |
你可以在这里详细阅读。好的。
将注册更改为:好的。
1 2 3 4 5 6 7 | 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); |
将代码从
1 2 3 4 5 6 7 8 9 10 11 | 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); } } |
基本上就是你需要的。好的。
但我们现在引入了一个新问题。我们现在使用服务定位器反模式来获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } } } |
现在,您可以在简单注入器
1 2 3 4 |
容器将提供一个依赖于
添加这个装饰器(及其注册)和改变生活方式将从您的示例中解决这个问题。好的。
不过,我希望你的申请最终会有一个
1 2 3 4 5 6 7 8 9 | public interface IService<T> { void Save(List<T> items); } public class MemberService : IService<Member> { // same code as before } |
您可以使用批注册在一行中注册所有实现:好的。
1 2 3 |
您可以将所有这些实例包装到上述
使用命令/处理程序模式会更好(您应该真正阅读链接!)对于这类工作。简而言之:在这个模式中,每一个用例都被转换成一个消息对象(一个命令),它由一个命令处理程序来处理,它可以由一个
您的示例如下所示:好的。
1 2 3 4 5 6 7 8 9 | public interface ICommandHandler<TCommand> { void Handle(TCommand command); } public class CreateMemberCommand { public string MemberName { get; set; } } |
使用以下处理程序:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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(); } } |
现在的形式取决于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 }); } } |
这些都可以注册如下:好的。
1 2 3 4 5 6 7 8 9 10 | 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); |
这种设计将完全消除对