简体   繁体   English

使用Autofac将对象注入MassTransit Saga

[英]Injecting objects into a MassTransit Saga using Autofac

I'm trying to get autofac to inject an factory into a saga on creation, and I can't get it working. 我正试图让autofac将一个工厂注入创造的传奇,我无法让它工作。 I've had no problem injecting the factory into a consumer so I know it's registered correctly, so I'm assuming I'm not registering the sagas correctly and autofac isn't building them up. 我没有把工厂注入消费者的问题所以我知道它已正确注册,所以我假设我没有正确注册传真,而autofac也没有建立它们。

Here's my registration code: 这是我的注册码:

var mapTypes = assembly.GetTypes()
   .Where(type => type.Implements(typeof(SagaClassMapping<>)));

builder.Register(context => GetSessionFactory(mapTypes)).AsSelf().SingleInstance();

// register all sagas and consumers
builder.RegisterAssemblyTypes(assembly)
   .Where(type => type.IsAssignableTo<ISaga>() || type.IsAssignableTo<IConsumer>())
   .AsSelf();

builder
    .RegisterGeneric(typeof(NHibernateSagaRepository<>))
    .As(typeof(ISagaRepository<>))
    .SingleInstance();

builder
    .Register(context => ServiceBusFactory.New(sbc =>
    {
        sbc.UseLog4Net();
        var queueUri = new Uri(ConfigurationManager.AppSettings["ReceiveQueue"]);
        var scope = context.Resolve<ILifetimeScope>();
        sbc.UseRabbitMq(transportConfig => 
            transportConfig.ConfigureHost(queueUri, hostConfig =>
            {
                hostConfig.SetUsername(ConfigurationManager.AppSettings["busUser"]);
                hostConfig.SetPassword(ConfigurationManager.AppSettings["busPassword"]);
            }));
        sbc.ReceiveFrom(queueUri);
        sbc.Subscribe(cfg => cfg.LoadFrom(scope));
    }))
    .SingleInstance();

The saga itself is pretty standard 传奇本身非常标准

public class MySaga : SagaStateMachine<MySaga>, ISaga
{
    public Guid CorrelationId { get; private set; }
    public Func<MyObject> ObjectBuilder { get; set; }
    public MySaga() { }

    public MySaga(Guid correlationId)
    {
        CorrelationId = correlationId;
    }

    Static MySaga()
    {
        Define(() =>
        { .... }
    }

I've tried adding the Func<MyObject> to a constructor, but it's not hit, it does work in a consumer so I know Autofac can build a Func<MyObject> . 我已经尝试将Func<MyObject>添加到构造函数中,但它没有被命中,它确实在消费者中工作,所以我知道Autofac可以构建一个Func<MyObject> I also tried using property injection with no luck: 我也尝试过使用属性注入而没有运气:

builder.RegisterAssemblyTypes(assembly)
   .Where(type => type.IsAssignableTo<ISaga>() || type.IsAssignableTo<IConsumer>())
   .PropertiesAutowired()
   .AsSelf();

and

builder.RegisterType<MySaga>()
    .OnActivated(arg => arg.Instance.MyBuilder =
        arg.Context.Resolve<Func<MyObject>>())
    .AsSelf();

Any help on what I'm doing wrong would be appreciated. 对我所做错的任何帮助都将不胜感激。


I got a reply from Chris Patterson on the masstransit-discuss group that pointed out I was probably doing it wrong. 我得到了Chris Patterson对masstransit-discuss小组的回复,指出我可能做错了。

Automatonymous is a better choice if you have dependencies, since the state machine and the state itself are separate classes. 如果你有依赖关系,Automatonymous是一个更好的选择,因为状态机和状态本身是单独的类。

Injecting dependencies into a class hydrated via NHibernate is never going to end well. 通过NHibernate将依赖关系注入到水合中的类永远不会结束。 There are a couple of helper classes that can be used to perform property-injection into the saga after it is loaded from NHibernate, the decorating saga repository I think has been posted here. 从NHibernate加载后,有几个辅助类可用于执行属性注入到saga中,我认为装饰传奇存储库已在此处发布。

Here is the example of the injecting repository for Magnum: https://github.com/MassTransit/MassTransit/blob/master/src/MassTransit.Tests/Saga/Injecting_Specs.cs 以下是Magnum注入存储库的示例: https//github.com/MassTransit/MassTransit/blob/master/src/MassTransit.Tests/Saga/Injecting_Specs.cs

Given it's NHibernate that's hydrating the object I should be looking there for the hook. 鉴于它的NHibernate正在保护物体,我应该在那里寻找钩子。 I've got a workaround for my current issue, but I'll post an answer here if/when I find one. 我的当前问题有一个解决方法,但如果/当我找到一个,我会在这里发布一个答案。

old question but it took a while for us to figure out a suitable way to wiring up sagas and autofac and then adding nhibernate on top of that. 旧的问题,但我们花了一段时间才找到一个合适的方法来连接传奇和autofac,然后添加nhibernate。

First create a wiring class (property injection) 首先创建一个布线类(属性注入)

public class MySagaRepository<TSaga> : ISagaRepository<TSaga> where TSaga : class, ISaga
    {
    private ISagaRepository<TSaga> _inner;
    private readonly IComponentContext _context;

    public MySagaRepository(ISagaRepository<TSaga> inner, IComponentContext context)
    {
        _inner = inner;
        _context = context;
    }

    public IEnumerable<Action<IConsumeContext<TMessage>>> GetSaga<TMessage>(IConsumeContext<TMessage> context, Guid sagaId, InstanceHandlerSelector<TSaga, TMessage> selector, ISagaPolicy<TSaga, TMessage> policy) where TMessage : class
    {
        return _inner.GetSaga(context, sagaId, (saga, message) =>
        {
            _context.InjectProperties(saga);
            return selector(saga, message);
        }, policy);
    }

    public IEnumerable<Guid> Find(ISagaFilter<TSaga> filter)
    {
        return _inner.Find(filter);
    }

    public IEnumerable<TSaga> Where(ISagaFilter<TSaga> filter)
    {
        return _inner.Where(filter).Select(x =>
        {
            _context.InjectProperties<TSaga>(x);
            return x;
        });
    }

    public IEnumerable<TResult> Where<TResult>(ISagaFilter<TSaga> filter, Func<TSaga, TResult> transformer)
    {
        return _inner.Where(filter, x =>
        {
            _context.InjectProperties(x);
            return transformer(x);
        });
    }

    public IEnumerable<TResult> Select<TResult>(Func<TSaga, TResult> transformer)
    {
        return _inner.Select(x =>
        {
            _context.InjectProperties(x);
            return transformer(x);
        });
    }
}

Then wire it 然后连线

builder.RegisterGeneric(typeof(NHibernateSagaRepository<>)).Named("innerRepo", typeof(ISagaRepository<>));
builder.RegisterGenericDecorator(typeof(MySagaRepository<>), typeof(ISagaRepository<>), fromKey: "innerRepo");

For the persistance it was just a matter of 对于持久性而言,这只是一个问题

public class SagaPersistenceHandler
{
    public static ISessionFactory CreateSessionFactory()
    {
        var provider = new SqlServerSessionFactoryProvider(ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString, new[]
        {
            typeof (MySagaMap)
        });

        return provider.GetSessionFactory();
    }
}

Now wire that 现在连线

builder.Register(c => SagaPersistenceHandler.CreateSessionFactory()).As<ISessionFactory>();

and the saga mapping to the saga (not included) 和传奇映射到传奇(不包括在内)

public MySagaMap()
    {
        SchemaAction(NHibernate.Mapping.ByCode.SchemaAction.None);
        Table("dbo.[tTable]");
        Property(x => x.Id);
        Property(x => x.CloseDate);
    }
}

All thats left is to register your saga 剩下的就是注册你的传奇

builder.RegisterType<MySaga>().AsSelf(); 

worked well (credits goes to @erikkallen) 工作得很好(学分归@erikkallen)

I made an adapter for Autofac to register and configure MT state machine sagas. 我为Autofac制作了一个适配器来注册和配置MT状态机传奇。

The repo is here . 回购在这里

It is available on nuget.org. 它可以在nuget.org上找到。

You can register your consumers and state machines by calling: 您可以通过以下方式注册您的消费者和州机器:

builder.RegisterSagaStateMachines(sagasAssembly);   // register all state machines
builder.RegisterConsumers(consumersAssembly);       // register consumers (standard MassTransit Autofac integration)

Note that RegisterConsumers is the standard MassTransit.Autofac extension. 请注意, RegisterConsumers是标准的MassTransit.Autofac扩展。

In your endpoint configuration you need to call the configuration like this: 在端点配置中,您需要调用以下配置:

x.ReceiveEndpoint(queueName, c =>
{
    c.LoadConsumers(container);
    c.LoadStateMachineSagas(container);
});

Pre-condition is that you have to register ISagaRepository implementations as well. 前提条件是您还必须注册ISagaRepository实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM