I'm trying to get autofac to inject an factory into a saga on creation, and I can't get it working. 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.
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>
. 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.
Automatonymous is a better choice if you have dependencies, since the state machine and the state itself are separate classes.
Injecting dependencies into a class hydrated via NHibernate is never going to end well. 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.
Here is the example of the injecting repository for 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. 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.
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)
I made an adapter for Autofac to register and configure MT state machine sagas.
The repo is here .
It is available on 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.
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.