加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc-4 – 使用StructureMap为NServiceBus和MVC管理Raven

发布时间:2020-12-16 03:23:36 所属栏目:asp.Net 来源:网络整理
导读:我在我们的解决方案中使用NServiceBus v4.3,MVC4,RavenDB 2.5和StructureMap 2.6.4. 我在StructureMap中遇到的类似于this question responses中描述的类似问题,我需要不同的生命周期用于MVC控制器和NServiceBus Handler在我的Web项目中使用RavenDB的IDocumen
我在我们的解决方案中使用NServiceBus v4.3,MVC4,RavenDB 2.5和StructureMap 2.6.4.

我在StructureMap中遇到的类似于this question responses中描述的类似问题,我需要不同的生命周期用于MVC控制器和NServiceBus Handler在我的Web项目中使用RavenDB的IDocumentSession.

特别是在我的情况下,如果我使用HybridHttpOrThreadLocalScoped(如上面的答案建议Windsor)生命周期,会话没有被妥善处理,我很快就达到了30个事务限制错误.如果我使用HttpContext生命周期,则不会调用Web项目中的NSB事件处理程序.

在我的控制器中,会话被包含在通过MVC ActionFilter应用的工作单元中.我也使用处理程序中的UoW,因为我的注册表已连线以从UoW检索会话.代码是这样的:

RavenDbWebRegistry.cs

public sealed class RavenDbWebRegistry : Registry
{
    public RavenDbWebRegistry()
    {
        // register RavenDB document store
        ForSingletonOf<IDocumentStore>().Use(() =>
        {
            var documentStore = new DocumentStore
            {
                ConnectionStringName = "RavenDB",Conventions =
                {
                    IdentityPartsSeparator = "-",JsonContractResolver = new PrivatePropertySetterResolver(),},};
            documentStore.Initialize();

            return documentStore;
        });


        For<IDocumentSession>().HybridHttpOrThreadLocalScoped().Add(ctx =>
        {
            var uow = (IRavenDbUnitOfWork)ctx.GetInstance<IUnitOfWork>();
            return uow.DocumentSession;
        });

        For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<WebRavenDbUnitOfWork>();            

    }
}

Web项目处理程序示例:

public class SiteCreatedEventHandler : IHandleMessages<ISiteCreatedEvent>
{
    public IBus Bus { get; set; }
    public IUnitOfWork Uow { get; set; }
    public IDocumentSession DocumentSession { get; set; }

    public void Handle(ISiteCreatedEvent message)
    {
        try
        {
            Debug.Print(@"{0}{1}",message,Environment.NewLine);

            Uow.Begin();
            var site = DocumentSession.Load<Site>(message.SiteId);
            Uow.Commit();

            //invoke Hub and push update to screen
            var context = GlobalHost.ConnectionManager.GetHubContext<AlarmAndNotifyHub>();

            //TODO make sure this SignalR function is correct
            context.Clients.All.displayNewSite(site,message.CommandId);
            context.Clients.All.refreshSiteList();            
        }
        catch (Exception ex)
        {                
            Uow.Rollback();
        }            
    }
}

ActionFilter的用法:

[RavenDbUnitOfWork]
    public ViewResult CreateNew(int? id)
    {
        if (!id.HasValue || id.Value <= 0)
            return View(new SiteViewModel { Guid = Guid.NewGuid() });

        var targetSiteVm = MapSiteToSiteViewModel(SiteList(false)).FirstOrDefault(s => s.SiteId == id.Value);

        return View(targetSiteVm);
    }

WebRegistry(在我的MVC项目中设置NSB)

public sealed class WebRegistry : Registry
{
    public WebRegistry()
    {
        Scan(x =>
        {
            x.TheCallingAssembly();
            x.Assembly("IS.CommonLibrary.ApplicationServices");
            x.LookForRegistries();
        });

        IncludeRegistry<RavenDbWebRegistry>();

        FillAllPropertiesOfType<IUnitOfWork>();
        FillAllPropertiesOfType<IDocumentSession>();
        FillAllPropertiesOfType<StatusConversionService>();
        FillAllPropertiesOfType<IStateRepository<TieState>>();
        FillAllPropertiesOfType<IStateRepository<DedState>>();
        FillAllPropertiesOfType<ITieService>();
        FillAllPropertiesOfType<IDedService>();
        FillAllPropertiesOfType<IHbwdService>();

        //NServiceBus
        ForSingletonOf<IBus>().Use(
        NServiceBus.Configure.With()
            .StructureMapBuilder()
            .DefiningCommandsAs(t => t.Namespace != null && t.Namespace.EndsWith("Command"))
            .DefiningEventsAs(t => t.Namespace != null && t.Namespace.EndsWith("Event"))
            .DefiningMessagesAs(t => t.Namespace == "Messages")
            .RavenPersistence("RavenDB")
            .UseTransport<ActiveMQ>()
            .DefineEndpointName("IS.Argus.Web")
            .PurgeOnStartup(true)
            .UnicastBus()
            .CreateBus()
            .Start(() => NServiceBus.Configure.Instance
            .ForInstallationOn<Windows>()
            .Install())
        );


        //Web             
        For<HttpContextBase>().Use(() => HttpContext.Current == null ? null : new HttpContextWrapper(HttpContext.Current));
        For<ModelBinderMappingDictionary>().Use(GetModelBinders());
        For<IModelBinderProvider>().Use<StructureMapModelBinderProvider>();
        For<IFilterProvider>().Use<StructureMapFilterProvider>();
        For<StatusConversionService>().Use<StatusConversionService>();
        For<ITieService>().Use<TieService>();
        For<IDedService>().Use<DedService>();
        For<IHbwdService>().Use<HbwdService>();
        For<ISiteService>().Use<SiteService>();

        IncludeRegistry<RedisRegistry>();
    }

我已经尝试使用我能想到的每种可能的组合配置我的注册表无济于事.

鉴于StructureMap混合生命周期不能像我期望的那样工作,我必须做些什么来实现正确的行为?

UoW对RavenDB是否必要/有益?我喜欢它(从我之前的NHibernate UoW ActionFilter中调整过它),因为它管理Controller Actions中会话的生命周期,但我对其他方法持开放态度.

理想情况下,我想要的是 – 在Web项目中 – 为控制器和处理程序分配完全不同的IDocumentSessions,但是无法以任何方式解决这个问题.

解决方法

首先,RavenDB已经通过包装IDocumentSession来实现工作单元,所以不需要它.打开一个会话,调用SaveChanges()并处理已完成的工作单元

其次,可以通过几种方式为控制器实现会话.

一般指导是在Global.asax.cs中设置商店.由于只有一个框架可以实现IDocumentSession – RavenDB,因此您也可以从Global实例化它.如果是存储库后面的NHibernate或Entity Framework,我明白了.但是IDocumentSession是RavenDB特有的,所以请在Application_Start中进行直接初始化.

public class Global : HttpApplication
{
   public void Application_Start(object sender,EventArgs e)
   {
      // Usual MVC stuff

      // This is your Registry equivalent,so insert it into your Registry file 
      ObjectFactory.Initialize(x=> 
      {
         x.For<IDocumentStore>()
          .Singleton()
          .Use(new DocumentStore { /* params here */ }.Initialize());
   }

   public void Application_End(object sender,EventArgs e)
   {
      var store = ObjectFactory.GetInstance<IDocumentStore>();

      if(store!=null)
         store.Dispose();
   }
}

在控制器中,添加基类,然后它可以为您打开和关闭会话. IDocumentSession再次特定于RavenDB,因此依赖注入实际上对您没有帮助.

public abstract class ControllerBase : Controller
{
   protected IDocumentSession Session { get; private set; }

   protected override void OnActionExecuting(ActionExecutingContext context)
   {
      Session = ObjectFactory.GetInstance<IDocumentStore>().OpenSession();
   }

   protected override void OnActionExecuted(ActionExecutedContext context)
   {
      if(this.IsChildAction)
         return;

      if(content.Exception != null && Session != null)
         using(context)
            Session.SaveChanges();
   }
}

然后从那里继承基础控制器并从那里开始工作:

public class CustomerController : ControllerBase
{
   public ActionResult Get(string id)
   {
      var customer = Session.Load<Customer>(id);

      return View(customer);
   }

   public ActionResult Edit(Customer c)
   {
      Session.Store(c);

      return RedirectToAction("Get",c.Id);
   }
 }

最后,我可以看到你正在使用StructureMap,因此只需要几个基本调用就可以从DI框架获取Session:

public class SiteCreatedEventHandler : IHandleMessages<ISiteCreatedEvent>
{
    public IBus Bus { get; set; }
    public IUnitOfWork Uow { get; set; }
    public IDocumentSession DocumentSession { get; set; }

    public SiteCreatedEventHandler()
    {
       this.DocumentSession = ObjectFactory.GetInstance<IDocumentStore>().OpenSession();
    }

    public void Handle(ISiteCreatedEvent message)
    {
       using(DocumentSession)
       {
          try
          {
             Debug.Print(@"{0}{1}",Environment.NewLine);

             ///// Uow.Begin(); // Not needed for Load<T>

             var site = DocumentSession.Load<Site>(message.SiteId);

             //// Uow.Commit(); // Not needed for Load<T>

             // invoke Hub and push update to screen
             var context = GlobalHost.ConnectionManager.GetHubContext<AlarmAndNotifyHub>();

             // TODO make sure this SignalR function is correct
             context.Clients.All.displayNewSite(site,message.CommandId);
             context.Clients.All.refreshSiteList();            
         }
         catch (Exception ex)
         {                
             //// Uow.Rollback(); // Not needed for Load<T>
         }            
      }
    }

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读