-
Notifications
You must be signed in to change notification settings - Fork 6
ASP.NET
Follow these steps to use CoreDdd in ASP.NET MVC application:
- Create a new ASP.NET MVC project.
- Install following nuget packages into the project:
- Pick you favourite IoC container to register CoreDdd services. CoreDdd supports Castle Windsor via following nuget packages:
And Ninject via following nuget packages:
For other IoC containers, manual registration of CoreDdd services into IoC container is needed.
- Add a new empty NHibernate configurator class, example:
public class CoreDddSampleNhibernateConfigurator : NhibernateConfigurator
{
protected override Assembly[] GetAssembliesToMap()
{
return new Assembly[0];
}
}
- Add a new NHibernate config file
hibernate.cfg.xml
. Here is SQLite example:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.connection_string">Data Source=CoreDddSampleAspNetWebApp.db</property>
<property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
<property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
<property name="connection.release_mode">on_close</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="show_sql">false</property>
<property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property>
<property name="transaction.use_connection_on_system_prepare">false</property> <!-- set transaction.use_connection_on_system_prepare to 'false' when using .NET 4.6.1+ or .NET Standard 2.0+, NHibernate 5.1.0+, more info here https://github.com/npgsql/npgsql/issues/1985#issuecomment-397266128 -->
</session-factory>
</hibernate-configuration>
Set hibernate.cfg.xml
Build Action to Content, and Copy to Output Directory to Copy if newer.
-
Install System.Data.SQLite.Core nuget package. Version 1.0.108 needs to be manually installed via command
Install-Package System.Data.SQLite.Core -Version 1.0.108
from Package Manager Console (Tools -> Nuget Package Manager -> Package Manager Console; current latest version 1.0.109.1 is broken). For different databases, please look at Entity database persistence. -
Add the following code into
MvcApplication.Application_Start()
inGlobal.asax.cs
to register CoreDdd services:
Castle Windsor IoC container:
var iocContainer = new WindsorContainer();
CoreDddNhibernateInstaller.SetUnitOfWorkLifeStyle(x => x.PerWebRequest);
iocContainer.Install(
FromAssembly.Containing<CoreDddInstaller>(),
FromAssembly.Containing<CoreDddNhibernateInstaller>()
);
iocContainer.Register(
Component
.For<INhibernateConfigurator>()
.ImplementedBy<CoreDddSampleNhibernateConfigurator>()
.LifestyleSingleton()
);
// register controllers
iocContainer.Register(
Classes
.FromAssemblyContaining<HomeController>()
.BasedOn<ControllerBase>()
.Configure(x => x.LifestyleTransient())
);
Ninject IoC container (needs also Ninject.Web.Common, Ninject.MVC5 nuget packages installed):
var iocContainer = new StandardKernel();
CoreDddNhibernateBindings.SetUnitOfWorkLifeStyle(x => x.InRequestScope());
iocContainer.Load(
typeof(CoreDddBindings).Assembly,
typeof(CoreDddNhibernateBindings).Assembly
);
iocContainer
.Bind<INhibernateConfigurator>()
.To<CoreDddSampleNhibernateConfigurator>()
.InSingletonScope();
// register controllers
iocContainer.Bind(x => x
.FromAssemblyContaining<HomeController>()
.SelectAllClasses()
.InheritedFrom<ControllerBase>()
.BindAllInterfaces()
.Configure(y => y.InTransientScope()));
For other IoC containers, check out the CoreDdd services and CoreDdd.Nhibernate services registrations for an inspiration how various CoreDdd services should be registered into an IoC container.
- To handle each web request within ADO.NET transaction:
Add the following code into web.config (under system.webServer - modules):
<add name="UnitOfWork" type="CoreDdd.AspNet.HttpModules.UnitOfWorkHttpModule, CoreDdd.AspNet" preCondition="managedHandler" />
and add the following code to MvcApplication.Application_Start()
to initialize UnitOfWorkHttpModule
:
Castle Windsor IoC container example:
UnitOfWorkHttpModule.Initialize(iocContainer.Resolve<IUnitOfWorkFactory>());
Ninject IoC container example:
UnitOfWorkHttpModule.Initialize(iocContainer.Get<IUnitOfWorkFactory>());
To handle each web request within a transaction scope with ADO.NET connection enlisted in the TransactionScope
,
add the following code into web.config (under system.webServer - modules) :
<add name="TransactionScopeUnitOfWork" type="CoreDdd.AspNet.HttpModules.TransactionScopeUnitOfWorkHttpModule, CoreDdd.AspNet" preCondition="managedHandler" />
and add the following code to MvcApplication.Application_Start()
to initialize TransactionScopeUnitOfWorkHttpModule
:
Castle Windsor IoC container example:
Action<TransactionScope> transactionScopeEnlistmentAction = transactionScope =>
{
// enlist custom resource manager into the transaction scope
};
TransactionScopeUnitOfWorkHttpModule.Initialize(
iocContainer.Resolve<IUnitOfWorkFactory>(),
transactionScopeEnlistmentAction: transactionScopeEnlistmentAction
);
Ninject IoC container example:
Action<TransactionScope> transactionScopeEnlistmentAction = transactionScope =>
{
// enlist custom resource manager into the transaction scope
};
TransactionScopeUnitOfWorkHttpModule.Initialize(
iocContainer.Get<IUnitOfWorkFactory>(),
transactionScopeEnlistmentAction: transactionScopeEnlistmentAction
);
- Add a controller factory into the project:
Castle Windsor IoC container example:
public class IoCContainerCastleWindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer _iocContainer;
public IoCContainerCastleWindsorControllerFactory(IWindsorContainer iocContainer)
{
_iocContainer = iocContainer;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, null);
}
var controller = (IController)_iocContainer.Resolve(controllerType);
return controller;
}
public override void ReleaseController(IController controller)
{
_iocContainer.Release(controller);
base.ReleaseController(controller);
}
}
Ninject IoC container example:
public class IoCContainerNinjectControllerFactory : DefaultControllerFactory
{
private readonly IResolutionRoot _iocContainer;
public IoCContainerNinjectControllerFactory(IResolutionRoot iocContainer)
{
_iocContainer = iocContainer;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, null);
}
var controller = (IController)_iocContainer.Get(controllerType);
return controller;
}
public override void ReleaseController(IController controller)
{
_iocContainer.Release(controller);
base.ReleaseController(controller);
}
}
And set the controller factory in MvcApplication.Application_Start()
:
Castle Windsor IoC container example:
ControllerBuilder.Current.SetControllerFactory(new IoCContainerCastleWindsorControllerFactory(iocContainer));
Ninject IoC container example:
ControllerBuilder.Current.SetControllerFactory(new IoCContainerNinjectControllerFactory(iocContainer));
The code above is available as a sample ASP.NET MVC application here. The sample application also contains ShipController with methods to create a new Ship aggregate root domain entity, query ships by name and update ship data. Once controllers, command handlers, query handlers and domain event handlers are added to the application, they needs to be registered into the IoC container:
Castle Windsor IoC container example:
// register command handlers
iocContainer.Register(
Classes
.FromAssemblyContaining<CreateNewShipCommandHandler>()
.BasedOn(typeof(ICommandHandler<>))
.WithService.FirstInterface()
.Configure(x => x.LifestyleTransient())
);
// register query handlers
iocContainer.Register(
Classes
.FromAssemblyContaining<GetShipsByNameQueryHandler>()
.BasedOn(typeof(IQueryHandler<>))
.WithService.FirstInterface()
.Configure(x => x.LifestyleTransient())
);
// register domain event handlers
iocContainer.Register(
Classes
.FromAssemblyContaining<ShipUpdatedDomainEventHandler>()
.BasedOn(typeof(IDomainEventHandler<>))
.WithService.FirstInterface()
.Configure(x => x.LifestyleTransient())
);
Ninject IoC container example:
// register command handlers
iocContainer.Bind(x => x
.FromAssemblyContaining<CreateNewShipCommandHandler>()
.SelectAllClasses()
.InheritedFrom(typeof(ICommandHandler<>))
.BindAllInterfaces()
.Configure(y => y.InTransientScope()));
// register query handlers
iocContainer.Bind(x => x
.FromAssemblyContaining<GetShipsByNameQueryHandler>()
.SelectAllClasses()
.InheritedFrom(typeof(IQueryHandler<>))
.BindAllInterfaces()
.Configure(y => y.InTransientScope()));
// register domain event handlers
iocContainer.Bind(x => x
.FromAssemblyContaining<ShipUpdatedDomainEventHandler>()
.SelectAllClasses()
.InheritedFrom(typeof(IDomainEventHandler<>))
.BindAllInterfaces()
.Configure(y => y.InTransientScope()));
You can check out other wiki pages how to persist an entity, query data, modify an application state or create more complex DDD implementation.