I'm trying to configure Castle Windsor with NHibernate and Castle.Facilities.AutoTx, but I keep getting problems with the PerTransactionLifeStyleOption. If I do
var sessionFactory = Container.Resolve<Func<ISession>>();
var session = sessionFactory();
I get the following exception (on the second line above):
Castle.Facilities.AutoTx.MissingTransactionException:
No transaction in context when trying to instantiate model 'NHibernate.ISession'
for resolve type 'NHibernate.ISession'. If you have verified that your call
stack contains a method with the [Transaction] attribute, then also make sure
that you have registered the AutoTx Facility.
at Castle.Facilities.AutoTx.Lifestyles.PerTransactionLifestyleManagerBase.Resolve(CreationContext context) in d:\Builds\Castle.Transactions-beta\src\Castle.Facilities.AutoTx\Lifestyles\PerTransactionLifestyleManagerBase.cs:line 153
at Castle.Facilities.AutoTx.Lifestyles.WrapperResolveLifestyleManager`1.Resolve(CreationContext context) in d:\Builds\Castle.Transactions-beta\src\Castle.Facilities.AutoTx\Lifestyles\WrapperResolveLifestyleManager.cs:line 143
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments)
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments)
at Castle.Facilities.TypedFactory.TypedFactoryComponent.Resolve(IKernel kernel)
at Castle.Facilities.TypedFactory.Resolve.Invoke(IInvocation invocation)
at Castle.Facilities.TypedFactory.TypedFactoryInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.Func`1Proxy.Invoke()
at IntegrationTest.NCVIB.WindsorIoC.LocalDbTest.get_Reader() in D:\Projects\NCVIB-GIT\NCVIB\src\IntegrationTest.NCVIB\WindsorIoC\LocalDbTest.cs:line 22
at IntegrationTest.NCVIB.InspectionObjectMapTests.ReadWrite() in D:\Projects\NCVIB-GIT\NCVIB\src\IntegrationTest.NCVIB\InspectionObjectMapTests.cs:line 34
Here's a compilation of the setup calls I'm making (They're actually spread out across several different installers):
container.AddFacility<AutoTxFacility>();
container.Register(Component.For<INHibernateInstaller>().Instance(new FluentNHibernateInstaller));
container.AddFacility<NHibernateFacility>(fac => fac.Option = DefaultSessionLifeStyleOption.SessionPerTransaction);
The FluentNHibernateInstaller is shown here:
public class FluentNHibernateInstaller : INHibernateInstaller
{
public FluentConfiguration BuildFluent()
{
return Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005
.DefaultSchema("dbo")
.ConnectionString(b => b.Is(ConnectionString ?? ConnectionStringChooser.GetConnectionString())))
.Cache(c => c.UseQueryCache().ProviderClass<SysCacheProvider>())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>().Conventions.AddFromAssemblyOf<EnumConvention>())
.ExposeConfiguration(
c => c.SetProperty(Environment.SqlExceptionConverter, typeof (MsSqlExceptionConverter).AssemblyQualifiedName))
.ExposeConfiguration(c => c.SetProperty(Environment.ShowSql, "true"));
}
public string ConnectionString { get; set; }
public void Registered(ISessionFactory factory)
{
}
public bool IsDefault
{
get { return true; }
}
public string SessionFactoryKey
{
get { return "default.sf"; }
}
public Maybe<IInterceptor> Interceptor
{
get { return Maybe.None<IInterceptor>(); }
}
}
It turns out that that the AutoTxFacility MUST be added to the container before any any component that has a [Transaction] attribute.
I was adding the facility in one of a few IWindsorInstaller classes in which the order of installation was "random" causing the facility to be installed AFTER some of my components with transactional methods:
var container = new WindsorContainer();
container.Install(FromAssembly.This);
But now I am adding the facility BEFORE installing from my installer classes:
var container = new WindsorContainer();
container.AddFacility<AutoTxFacility>();
container.Install(FromAssembly.This());
Here is what the usage would be like:
public class MyClass
{
private readonly Func<ISession> _sessionFactory;
public MyClass(Func<ISession> sessionFactory)
{
_sessionFactory = sessionFactory;
}
[Transaction]
public virtual void UseTheSessionForSomething()
{
// The transaction (and session) will be initialized
// because of the
// [Transaction] attribute
// AND this method is virtual
// AND this instance was resolved from the container
MethodWithinTransactionScope();
}
public void MethodWithinTransactionScope()
{
// Method just needs to be invoked in the scope of a transaction
var session = _sessionFactory();
session.Get<Entity>(1);
}
}
public void Test()
{
var myInstance = container.Resolve<MyClass>();
myInstanace.UseTheSessionForSomething();
}
Is the exception says; the answer is to add a transaction around the method with the line of code: var session = sessionFactory();
If you are getting the exception and HAVE added [Transaction] on that method, then you are not resolving the service LocalDbTest properly.
Have a look at my quick-start for a quick-start:
https://github.com/haf/Castle.Facilities.NHibernate/wiki/NHibernate-Facility---Quick-Start
I'm reading your code now ;), the reason that it was missing was because of this line:
https://github.com/haf/Castle.Transactions/blob/master/src/Castle.Facilities.AutoTx/AutoTxFacility.cs#L86
It's a known 'TODO' that you have to register your facility before your components, and I think this is stated in the quickstart as well.
Cheers
Related
I followed this article and got everything working except dependency inject (partially). In my project I am using unity and I am trying to create a custom Transaction attribute the purpose of which is to start a NHibernate transaction before the execution of an action and commit/rollback the transaction after the method execution.
This is the definition of my attribute:-
public class TransactionAttribute : Attribute
{
}
Following is the definition of my TransactionFilter
public class TransactionFilter : IActionFilter
{
private readonly IUnitOfWork _unitOfWork;
public TransactionFilter(IUnitOfWork uow) {
_unitOfWork = uow;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault();
if (transAttribute == null) {
return continuation();
}
var transaction = uow.BeginTransaction();
return continuation().ContinueWith(t =>
{
try{
transaction.Commit();
return t.Result;
}
catch(Exception e)
{
transaction.Rollback();
return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result;
}
}
}
}
And I have created a custom filter provider which uses unity to construct this filter.
public class UnityActionFilterProvider
: ActionDescriptorFilterProvider,
IFilterProvider
{
private readonly IUnityContainer container;
public UnityActionFilterProvider(IUnityContainer container)
{
this.container = container;
}
public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>())
{
// TODO: Determine correct FilterScope
yield return new FilterInfo(actionFilter, FilterScope.Global);
}
}
}
I register the UnityActionFilterProvider in UnityWebApiActivator (I am using Unity.AspNet.WebApi package) as follows
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
var resolver = new UnityDependencyResolver(container);
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = resolver;
var providers = config.Services.GetFilterProviders();
var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider);
config.Services.Remove(typeof(IFilterProvider), defaultProvider);
config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container));
}
The problem is everything works ok for the first request for any action but subsequent requests for the same action doesn't recreate the TransactionFilter which means it doesn't call the constructor to assign a new UOW. I don't think I can disable the action filter caching.
The only option I have got now is to use the service locator pattern and get UOW instance using container inside ExecuteActionFilterAsync which in my opinion kills the purpose of this and I am better off implementing custom ActionFilterAttribute.
Any suggestions ?
As far as I've been able to tell during the years, what happens in web application startup code essentially has Singleton lifetime. That code only runs once.
This means that there's only a single instance of each of your filters. This is good for performance, but doesn't fit your scenario.
The easiest solution to that problem, although a bit of a leaky abstraction, is to inject an Abstract Factory instead of the dependency itself:
public class TransactionFilter : IActionFilter
{
private readonly IFactory<IUnitOfWork> _unitOfWorkFactory;
public TransactionFilter(IFactory<IUnitOfWork> uowFactory) {
_unitOfWorkFactory = uowFactory;
}
// etc...
Then use the factory in the ExecuteActionFilterAsync method:
var transaction = _unitOfWorkFactory.Create().BeginTransaction();
A more elegant solution, in my opinion, would be to use a Decoraptor that Adapts the TransactionFilter, but the above answer is probably easier to understand.
I am using NServiceBus v4.3, MVC4, RavenDB 2.5 and StructureMap 2.6.4 in our solution.
I am having a similar issue under StructureMap to that described in this question's responses where I require different lifecycles for the MVC Controller and NServiceBus Handler use of RavenDB's IDocumentSession in my Web project.
Specifically in my case what happens is that if I use the HybridHttpOrThreadLocalScoped (as the above answer suggests for Windsor) lifecycle the sessions are not properly disposed of and I soon hit the 30 transaction limit error. If I use the HttpContext lifecycle my NSB event Handlers in the Web project do not get called.
In my Controllers the session is wrapped in a unit of work applied via an MVC ActionFilter. I also use the UoW within the Handlers as my Registry is wired up to retrieve the session from the UoW. The code is as such:
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>();
}
}
Example of Web project Handler:
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();
}
}
}
Usage of 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 (that sets up NSB in my MVC project)
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>();
}
I have tried configuring my Registry using every possible combination I can think of to no avail.
Given that the StructureMap hybrid lifecycle does not work as I would expect, what must I do to achieve the correct behaviour?
Is the UoW necessary/beneficial with RavenDB? I like it (having adapted it from my earlier NHibernate UoW ActionFilter) because of the way it manages the lifecycle of my sessions within Controller Actions, but am open to other approaches.
What I would ideally like is a way to - within the Web project - assign entirely different IDocumentSessions to Controllers and Handlers, but have been unable to work out any way to do so.
Firstly, RavenDB already implements unit of work by the wrapping IDocumentSession, so no need for it. Opening a session, calling SaveChanges() and disposing has completed the unit of work
Secondly, Sessions can be implemented in a few ways for controllers.
The general guidance is to set up the store in the Global.asax.cs. Since there is only 1 framework that implements IDocumentSession - RavenDB, you might as well instantiate it from the Global. If it was NHibernate or Entity Framework behind a repository, I'd understand. But IDocumentSession is RavenDB specific, so go with a direct initialization in the 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();
}
}
In the Controllers, add a base class and then it can open and close the sessions for you. Again IDocumentSession is specific to RavenDB, so dependency injection doesn't actually help you here.
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();
}
}
Then from there, inherit from the base controller and do your work from there:
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);
}
}
Finally, I can see you're using StructureMap, so it only takes a few basic calls to get the Session from the DI framework:
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}", message, 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>
}
}
}
I am trying to use Ninject to inject an EventLogger instance into a custom ExceptionFilterAttribute. Whenever I run the code, the EventLogger instance is null. I have implemented an IFilterProvider to resolve dependencies in a similar manner for my custom AuthorizationFilterAttribute, and that works fine. Any ideas?
Not Working
public class ErrorHandlingAttribute : ExceptionFilterAttribute
{
[Inject]
public IEventLogger EventLogger { get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
EventLogger.LogException(actionExecutedContext.Exception);
actionExecutedContext.Response = actionExecutedContext.Request.
CreateResponse(HttpStatusCode.BadRequest,
new ServiceErrorResponseDTO("An unhandled exception occurred while calling " +
actionExecutedContext.Request.RequestUri.ToString() +
". This event has been logged. If you continue to receive this error contact Weichert"));
}
}
Working
public class RequireAuthorizationAttribute : AuthorizationFilterAttribute
{
[Inject]
public IServiceRepository ServiceRepository { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
#region Header Authentication
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
Custom IFilterProvider
public class NinjectWebApiFilterProvider : IFilterProvider
{
private IKernel _kernel;
public NinjectWebApiFilterProvider(IKernel kernel)
{
_kernel = kernel;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var controllerFilters = actionDescriptor.ControllerDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Controller));
var actionFilters = actionDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Action));
var filters = controllerFilters.Concat(actionFilters);
foreach(var filter in filters)
{
_kernel.Inject(filter.Instance);
}
return filters;
}
}
NinjectWebCommon CreateKernel Method
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Ad Ninject support for Web API.
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider),
new NinjectWebApiFilterProvider(kernel));
RegisterServices(kernel);
return kernel;
}
NinjectWebCommon Bindings
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ServiceDbContext>().To<ServiceDbContext>();
kernel.Bind<IServiceRepository>().To<ServiceRepository>();
kernel.Bind<CareerDevelopmentDbContext>().To<CareerDevelopmentDbContext>();
kernel.Bind<ICareerDevelopmentRepository>().To<CareerDevelopmentRepository>();
kernel.Bind<ICareerDevelopmentService>().To<CareerDevelopmentService>();
kernel.Bind<IEventLogger>().To<ServiceEventLogger>();
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>().WithConstructorArgument("kernel", kernel);
}
I had the same problem and was configuring my error handler the same way by adding it to the filter collection in WebApiConfig.cs which meant it wasn't getting handled by the FilterProvider implementation I had added. So I did this instead:
public class LoggingExceptionFilterAttribute : ExceptionFilterAttribute, IExceptionFilter
{
// this is what I wanted injected
private IEmailService emailService;
public LoggingExceptionFilterAttribute(IEmailService service)
{
emailService = service;
}
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
// my implementation here
}
}
Then I registered this in NinjectWebCommon like so:
kernel.Bind<System.Web.Http.Filters.IExceptionFilter>().To<LoggingExceptionFilterAttribute>().InSingletonScope();
And then in WebApiConfig I realized that I could get a hold of the DependencyResolver so I did this:
config.Filters.Add((IFilter)config.DependencyResolver.GetService(typeof(IExceptionFilter)));
Now Ninject handles constructing my exception filter and I can even do constructor injection instead of needing [Inject] attributes and I don't have to add my ExceptionFilterAttribute to every API controller.
Ok, you have to make sure you are binding your custom IFilterProvider as well. As of writing the Ninject.Web.WebApi Nuget package is unstable and would do that automatically for you, if you were using it. Just in the same fashion Ninject.MVC3 does this for your regular controllers.
Just make sure you have this binding, and the replaced DependencyResolver will look for IFilterProvider implementation via your Ninject kernel as well:
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>();
Then your NinjectWebApiFilterProvider will kick in and inject dependencies into your filters as per your code.
I'm trying to implement a service that will run jobs based on Quartz.Net. The jobs may have dependencies like IRepository<> and the repository implementation will have a NHibernate ISession injected into it. (Quartz will be hosted in a Windows Service). Jobs are resolved via a IJob factory implementation that uses Ninject to resolve (currently wrapped in a IServiceLocator implementation).
Job Scope
I would like to be able to use Ninject to scope the ISession per Job so that there is one session created per job that may be used in multiple IRepository<>'s .
Not sure if this is possible but am wondering if anyone has experience with this?
Can I somehow use the Job context to create a Scope that is used by Kernel.InScope(???).
Quartz.Net IJobFactory:
public class JobFactory : IJobFactory
{
readonly IServiceLocator locator;
public JobFactory(IServiceLocator locator)
{
this.locator = locator;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var jobDetail = bundle.JobDetail;
var jobType = jobDetail.JobType;
return (IJob)locator.Resolve(jobType);
}
catch (Exception e)
{
var se = new SchedulerException("Problem instantiating class", e);
throw se;
}
}
}
Ninject Bindings:
//Service Locator
Bind<IServiceLocator>().To<NinjectAdapter>();
//Quartz Bindings
Bind<IJobFactory>().To<JobFactory>();
//NHibernate Bindings
Bind<ISessionFactory>().ToMethod(ctx => ctx.Kernel.Get<NHibernateConfiguration>().BuildSessionFactory()).InSingletonScope();
Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession());// ToDo: Figure out how to scope session
//Repository Bindings
Bind(typeof (IRepository<>)).To(typeof (ReadWriteRepository<>));
Main Execution:
InitializeIoC();
scheduler = schedulerFactory.GetScheduler();
scheduler.JobFactory = ServiceLocator.Resolve<IJobFactory>();
InitializeJobs();
scheduler.Start();
Example Job:
public class TestJob3 : IJob
{
private readonly IRepository<Customer> repo;
private readonly IRepository<Order> orderRepo;
public TestJob3(IRepository<Customer> repo, IRepository<Order> orderRepo)
{
//orderRepo and repo should have the same ISession
this.repo = repo;
this.oderRepo = orderRepo;
System.Diagnostics.Debug.WriteLine("Job 3 Created");
}
#region Implementation of IJob
public void Execute(IJobExecutionContext context)
{
System.Diagnostics.Debug.WriteLine("Job 3 Executing");
using (var scope = new TransactionScope())
{
var customer = repo.GetById(1);
customer.Name = "Blue Goats";
repo.Save(customer);
scope.Complete();
}
}
#endregion
}
** Repository Snippet: **
public class ReadWriteRepository<TEntity> : IRepository<TEntity> where TEntity : class, IRootEntity
{
private readonly ISession session;
public ReadWriteRepository(ISession session)
{
this.session = session;
}
public virtual TEntity GetById(int id)
{
var entity = session.Get<TEntity>(id);
return entity;
}
public virtual TEntity Save(TEntity entity)
{
session.SaveOrUpdate(entity);
return entity;
}
}
Thanks for taking the time!
Update
I ended up using Remo's suggestion and am using InCallScope():
Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession()).InCallScope();
The way I like to think of it (correct or not?) is everything from the "initial" get reuses the same items throughout the dependency tree
Use InCallScope
https://github.com/ninject/ninject.extensions.namedscope/wiki/InCallScope
NHibernate 3.2/Fluent NHibernate 1.3/StructureMap 2.6.3 -
Trying to follow DDD as an architectural strategy, I typically don't have dependencies on domain entities. However, I'm experimenting right now with adding more behavior to my domain entities so that they are not so anemic. Everything was going well until I hooked up NHibernate. I've got two issues:
NH requires a parameterless constructor and I'd rather not have a
ctor that shouldn't be used.
When NH tries to instantiate my entity, it needs to resolve my
dependencies but I haven't given NH anything with which it can do
that.
I've been reading on the web, but most (if not all) of the examples I have found are outdated (or just old). Even though the NH camp probably doesn't approve of what I'm doing, I'm looking for the NH way to do this.
The solution ended up an implementation of NHibernate's IInterceptor. It is actually a very simple implementation when you inherit from EmptyInterceptor and override JUST the Instantiate() and SetSession() methods. Here's my interceptor using StructureMap:
public class DependencyInjectionEntityInterceptor : EmptyInterceptor
{
IContainer _container;
ISession _session;
public DependencyInjectionEntityInterceptor(IContainer container)
{
_container = container;
}
public override void SetSession(ISession session)
{
_session = session;
}
public override object Instantiate(string clazz, EntityMode entityMode, object id)
{
if (entityMode == EntityMode.Poco)
{
var type = Assembly.GetAssembly(typeof (SomeClass)).GetTypes().FirstOrDefault(x => x.FullName == clazz);
var hasParameters = type.GetConstructors().Any(x => x.GetParameters().Any());
if (type != null && hasParameters)
{
var instance = _container.GetInstance(type);
var md = _session.SessionFactory.GetClassMetadata(clazz);
md.SetIdentifier(instance, id, entityMode);
return instance;
}
}
return base.Instantiate(clazz, entityMode, id);
}
}
Then, all you have to do is tell NHibernate to use your interceptor:
public FluentConfiguration GetFluentConfiguration(IContainer container)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("Database"))
.ShowSql())
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<SomeClass>()))
.ExposeConfiguration(x =>
x.SetInterceptor(new DependencyInjectionEntityInterceptor(container)));
}
When I was researching this, some suggested passing in the SessionFactory into the ctor of the interceptor class. Honestly, from a session management perspective, this approach would be better.
If you need additional dependencies in your entities don't use constructor injection. Instead create an additional parameter in the entity method.
Now you will ask yourself how do you get the dependency. For this you can use CommandHandlers and Commands. The command handler takes the dependency within its constructor and calls the method of the entity. In the UI you create a command message and send it to a command processor which is responsible for calling the correct command handler.
I hope my explanation is comprehensible to you.
Domain:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public void SendNotification(string message, INotifier notifier)
{
notifier.SendMessage(string.Format("Message for customer '{0}' ({1}): {2}", Name, Id, message));
}
}
The INotifier infrastructure component is passed through the method and not the constructor!
Infrastructure:
public interface INotifier
{
void SendMessage(string message);
}
class EmailNotifier : INotifier
{
public void SendMessage(string message)
{
// SmtpClient...
}
}
class SMSNotifier : INotifier
{
public void SendMessage(string message)
{
// SMS ...
}
}
Command and CommandHandler:
public class NotificationCommandHandler : ICommandHandler<NotificationCommand>
{
private readonly INotifier _notifier;
public NotificationCommandHandler(INotifier notifier)
{
_notifier = notifier;
}
public void Execute(NotificationCommand commandMessage)
{
commandMessage.Employee.SendNotification(commandMessage.Message, _notifier);
}
}
public class NotificationCommand
{
public string Message { get; set; }
public Employee Employee { get; set; }
}
The CommandHandler gets the INotifier through constructor injection. So you do not need to use your IoC Container like a ServiceLocator.
Usage i.e. in the UI in a controller:
public class Controller
{
private readonly IMessageProcessor _messageProcessor;
public Controller(IMessageProcessor messageProcessor)
{
_messageProcessor = messageProcessor;
}
public void SendNotification (Employee employee, string message)
{
var sendMailCommand = new NotificationCommand
{
Employee = employee,
Message = message
};
_messageProcessor.Process(sendMailCommand);
}
}
If you have questions about the command processor have a look at the mvccontrib project or ask a separate question.
Sorry my previous answer didn't address the specific question. I did some more research, and it looks like I have much more to learn about when and when not to use an anemic domain model. Regarding your question, I found this article to be very on topic. It is on java, not c#, but the principles are the same. Hope this helps.