【问题标题】:How should you use UnitofWork pattern on my asp.net-mvc site (using nhibernate and ninject)你应该如何在我的 asp.net-mvc 站点上使用 UnitofWork 模式(使用 nhibernate 和 ninject)
【发布时间】:2023-12-09 15:49:01
【问题描述】:

我有 followed the pattern on this site 将 ninject 和休眠连接到我的 asp.net-mvc3 站点。

这是我 global.aspx.cs 中的代码:

 internal class ServiceModule : NinjectModule
{
    public override void Load()
    {
        var helper = new NHibernateHelper(connectionString);
        Bind<ISessionFactory>().ToConstant(helper.SessionFactory)
            .InSingletonScope();

        Bind<IUnitOfWork>().To<UnitOfWork>()
            .InRequestScope();
        Bind<ISession>().ToProvider(new SessionProvider())
            .InRequestScope();
        Bind<IIntKeyedRepository<FAQ>>().To<Repository<FAQ>>()
            .InRequestScope();
       }

问题是我现在需要在我的控制器中执行 Update() 和 Add();

我有这个作为我的控制器代码:

    public FAQController(IIntKeyedRepository<FAQ> faqRepository, IUnitOfWork unitOfWork)
    {
        _faqRepository = faqRepository;
        _unitOfWork = unitOfWork;
    }


  [Authorize]
    [AcceptVerbs(HttpVerbs.Post)]
    [ValidateInput(false)]
    public ActionResult AddFAQ(FAQ contact)
    {
        var c = new FAQ {Question = contact.Question, Answer = contact.Answer};
        _faqRepository.Add(c);
        _unitOfWork.Commit();
        return RedirectToAction("Index");
    }

我的主要问题是在构造函数中传入 Iunitofwork 感觉不对,因为许多其他操作不需要它。我只真正需要它来执行更新和插入数据库的操作。由于我在上面的链接上使用了 ninject IOC,因此似乎要通过 IOC 传递这个 unitofwork 对象。

那么,在 asp.net-mvc 中使用带有 IOC 的 UnitOfWork 模式是否有更好更优化的方法,它确实为我的控制器中的每个方法调用beingtransaction。

【问题讨论】:

  • 看看this的博文。

标签: nhibernate asp.net-mvc-3 ioc-container ninject unit-of-work


【解决方案1】:

另一种处理事务的方法是使用IActionFilterOnActionExecuting 中打开事务并在OnActionExecuted 上提交

public class TransactionFilter : IActionFilter
{
    private readonly ISession session;
    private ITransaction transaction;

    public TransactionFilter(ISession session)
    {
        this.session = session;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        this.transaction = this.session.BeginTransaction();
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        try
        {
            if (this.transaction.IsActive)
            {
                if (filterContext.Exception == null)
                {
                    this.transaction.Commit();
                }
                else
                {
                    this.transaction.Rollback();
                }
            }
        }
        finally
        {
            this.transaction.Dispose();
        }
    }
}

定义一个属性来标记使用事务的操作:

[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : Attribute
{ 
}

更改您的 Ninject 配置:

internal class ServiceModule : NinjectModule
{
    public override void Load()
    {
        var helper = new NHibernateHelper(connectionString);
        Bind<ISessionFactory>().ToConstant(helper.SessionFactory)
            .InSingletonScope();

        Bind<ISession>().ToProvider<SessionProvider>().InRequestScope();
        Bind(typeof(IRepository<>)).To(typeof(Repository<>));
        Bind(typeof(IIntKeyedRepository<>)).To(typeof(Repository<>));
        BindFilter<TransactionFilter>(FilterScope.Action, null)
            .WhenActionMethodHas<TransactionAttribute>();
    }
}

最后改变你的控制器:

public FAQController(IIntKeyedRepository<FAQ> faqRepository)
{
    _faqRepository = faqRepository;
}


[Transaction]
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
public ActionResult AddFAQ(FAQ contact)
{
    var c = new FAQ {Question = contact.Question, Answer = contact.Answer};
    _faqRepository.Add(c);
    return RedirectToAction("Index");
}

【讨论】:

  • 这正是我想要的。 Ayende 提出了几乎相同的解决方案:ayende.com/blog/4809/… 但我更喜欢注入会话而不是从控制器请求。
【解决方案2】:

我通常尝试将我的通用 IRepository 实现隐藏在 IUnitOfWork 中(见下文)。

我的另一个建议是将 UnitOfWorkProvider 或 UnitOfWorkFactory 传递给构造函数。这样您就可以在本地注册事务范围。这具有额外的好处,即能够通过依赖注入或手动解决您认为合适的 IRepository 或 ISession。

using(var uow = this.UnitOfWorkProvider.New())
{
    uow.Save<Faq>(myFaq);
}

还要确保您在 IUnitOfWork.Dispose() 中清理了事务以及您可能拥有的任何数据会话对象/信息。

【讨论】:

    【解决方案3】:

    我更喜欢只将我的工作单元注入到实际使用它们的类中。在大多数情况下,持久性类(在我的例子中是存储库)是唯一需要工作单元的类。您要确保保持关注点的清晰分离。控制器不需要知道工作单元,也不应该与它耦合。

    public class FaqRepository {
      public FaqRepository(IUnitOfWork unitofWork) { ... }
    
      public void CreateQuestion(Faq faq) {
        unitOfWork.Save(faq);
        unitOfWork.Commit();
      }
    }
    

    如果您从控制器调用存储库,请将存储库注入控制器,如下所示:

    public class FaqController {
      public FaqController(IFaqRepository faqRepository) {...}
    }
    

    这有意义吗?

    【讨论】:

    • 我看到的问题是您的存储库现在负责 UoW。如果您需要进行需要两个存储库的更改,您将遇到问题。
    • 如果您的 UoW 属于两个对象,例如 UserLogin 和 UserProfile 分别存储,没有级联,它跨越两个存储库,并且您希望在事务中运行这两个对象。您的存储库当前提交事务,因此您需要运行两个单独的工作单元。
    • @Phil 有道理。你会在哪里处理这笔交易?这是您在服务/业务层中要做的事情吗?服务/业务层依赖于你的 UoW 以便它可以管理事务是否有意义?如果您实现了每请求会话模式并在请求期间打开事务会怎样?
    • 我处理它的方式是逐个操作。在我的 BaseController 中,我覆盖 OnActionExecuting 以创建一个新的 UnitOfWork,并覆盖 OnActionExecuted 我调用 Commit。我将 UnitOfWork 传递给使用它的方法(服务和/或存储库)。哦,OnException 我回滚了提交:)
    • 给我发一封电子邮件到博客 [at] philliphaydon.com,我会将我的 BaseController 发送给您,以便您查看。
    最近更新 更多