I am trying to use the Unity IoC with the unit of work & repository patterns, however I'm struggling to work out how my repository would get it's Nhibernate session to perform it's work...
Below I have my IUnityContainer which does my IoC bindings.. I then have my NHibernate repo which needs to be provided with a ISession, which should be provided from the NHibernate unit of work, but I am now sure how I can tell Unity to provide it in it's constructor..
I have made a binding for the IUnitOfWork to a Nhib... one, but how does one pass that value in the IRepository RegisterType?
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
NHibernateHelper helper = new NHibernateHelper();
// register all your components with the container here
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterControllers();
container.RegisterType<IUnitOfWork, NHibernateUnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterInstance<ISessionFactory>(helper.SessionFactory);
container.RegisterType<IRepository, NHibernateRepository>() ;
return container;
}
public class NHibernateRepository : IRepository
{
public NHibernateRepository(ISession session)
{
_session = session;
}
}
public class NHibernateUnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public ISession Session { get; private set; }
public NHibernateUnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
Session = _sessionFactory.OpenSession();
Session.FlushMode = FlushMode.Auto;
_transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted);
}
}
Register your ISession with your IUnityContainer like so:
container.RegisterType<ISession>(new InjectionFactory(c => c.Resolve<ISessionFactory>().OpenSession());
Then when an ISession is required, an ISessionFactory will be resolved and a session started.
In the applications I work on, we define our repositories and unit of work like so:
public class NhUnitOfWork : IUnitOfWork
{
readonly ISession _session;
public IRepository<T> GetRepository<T>() where T : class
{
return new NhRepository<T>(_session);
}
public NhUnitOfWork(ISession session)
{
_session = session;
}
public void Dispose()
{
// Dispose logic, i.e. save/rollback
}
}
public class NhRepository<T> : IRepository<T> where T : class
{
readonly ISession _session;
public void Add(T item)
{
_session.Save(item);
}
public void Delete(T item)
{
_session.Delete(item);
}
public void Update(T item)
{
_session.Update(item);
}
public NhRepository(ISession session)
{
_session = session;
}
}
Then your services use it something like this:
public class MyService
{
readonly Func<IUnitOfWork> _unitOfWorkFactory;
public MyService(Func<IUnitOfWork> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public void DoServiceStuff()
{
using(var uow = _unitOfWorkFactory())
{
var newUser = new User() { Username = "My User" };
var userRepo = uow.GetRepository<User>();
userRepo.Add(newUser);
uow.Save();
}
}
}
You want to create instance of "IRepository", you need to initialize mapping for all it dependencies. It's single: "ISession"
Related
I write an application with WPF. I use the Prism library with IoC as Prism.DryIoC.
I have an AppDbContext.cs class to declare the connection string to the database (here is MongoDB)
public class AppDbContext : BaseMongoRepository
{
public AppDbContext(string connectionString, string databaseName = null) : base(connectionString, databaseName)
{
}
}
I have a class MyService.cs that uses the AppDbContext class, I declare in the constructor.
public class MyService : IMyService
{
private AppDbContext _dbContext;
public IdentifierRepository(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public void AddCustomer(Customer model)
{
// Some code....
_dbContext.Add(model);
}
}
In the App.xaml.cs class I override the method
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IAuthenticationService, AuthenticationService>();
// MongoDB
var connectionString = SharedCommon.LocalAppSettings.Database.ConnectionString;
var database = SharedCommon.LocalAppSettings.Database.DatabaseName;
// How to register class MyService.cs here?
// I dont known.
containerRegistry<MyService>(() => new MyService(new AppDbContext(connectionString, database))); // Wrong code
}
You can find all the registration methods here.
For singleton MyService:
var myService = new MyService(new AppDbContext(connectionString, database)));
containerRegistry.RegisterInstance(myService);
For multiple instances you could use a factory instead.
public class MyServiceFactory
{
private readonly AppDbContext appDbContext;
public MyServiceFactory(AppDbContext appDbContext)
{
this.appDbContext = appDbContext;
}
public MyService Create() => new MyService(appDbContext);
}
Register the instance of the factory:
var context = new AppDbContext(connectionString, database);
var factory = new MyServiceFactory(context);
containerRegistry.RegisterInstance(factory);
Then create your service instance:
var factory = container.Resolve<MyServiceFactory>();
var service = factory.Create();
I have a WindowsForm Project With this design :
DAL(GenericRepository => UnitOfWork) => BLL(Service) => UI
And use EntityFramWork, Interface, GenericRepository, Dependency Injection
My Code in Repository(DAL) :
public class Repository : RepositoryBase, IDisposable, IRepository where T : class
{
private readonly DbSet dbSet;
private bool disposed = false;
public Repository(GlobalERPEntities dbContext)
{
DBContext = dbContext;
dbSet = DBContext.Set();
}
public virtual IEnumerable GetAll()
{
return dbSet.ToList();
}
}
UnitOfWork(DAL) :
public class UnitOfWork : RepositoryBase, IUnitOfWork, IDisposable
{
private Dictionaryobject> repositories;
private bool disposed = false;
public UnitOfWork(GlobalERPEntities dbContext)
{
DBContext = dbContext;
}
public IRepository Repository() where T : class
{
if (repositories == null)
{
repositories = new Dictionaryobject>();
}
if (repositories.Keys.Contains(typeof(T)) == true)
{
return repositories[typeof(T)] as Repository;
}
Repository repo = new Repository(DBContext);
repositories.Add(typeof(T), repo);
return repo;
}
Service(BLL) :
public class Service_HR_Person : IService_HR_Person , IDisposable
{
private readonly IUnitOfWork UnitOfWork;
public Service_HR_Person(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
public virtual IEnumerable GetAll()
{
return UnitOfWork.Repository().GetAll().ToList();
}
MyForm(UI) :
using (Service_HR_Person srvPerson = new Service_HR_Person())
{
srvPerson.Delete(base.rowid);
try
{
srvPerson.Save();
MessageManager.Show(Enums.MessageBoxType.InformationTransactionSuccessfully);
}
catch (Exception ex)
{
MessageManager.Show(ErrorManager.ProccessException(ex), Enums.MessageBoxType.Error);
}
}
Iknow should not use DAL Layer in UI layer and BLL is between DAL and UI
but i have error in ui
using (Service_HR_Person srvPerson = new Service_HR_Person())
"new Service_HR_Person()" say need an arguman in () that is unitofwork but we should not use unitofwork in UI
i read some article and sample that use IOC , ninject ,bootstraper and ... but i cant write true code
How To use Ninject or IOC?
please help me with code
thankyou
add a new project to solution with name "Configure"
add castle.windsor from NuGet to all project
add a class to this project with name "Bootstrapper" and write this code
public static WindsorContainer Container = null;
public static void WireUp()
{
Container = new WindsorContainer();
Container.Register(Component.For<GlobalERPEntities>());
Container.Register(Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>());
Container.Register(Component.For<IService_HR_Person>().ImplementedBy<Service_HR_Person>());
}
and edit your code in UI
using (Service_HR_Person srvPerson = Bootstrapper.Container.Resolve<Service_HR_Person>())
{
srvPerson.Delete(base.rowid);
try
{
srvPerson.Save();
RemoveRow();
MessageManager.Show(Enums.MessageBoxType.InformationTransactionSuccessfully);
}
catch (Exception ex)
{
MessageManager.Show(ErrorManager.ProccessException(ex), Enums.MessageBoxType.Error);
}
}
this line
using (Service_HR_Person srvPerson = Bootstrapper.Container.Resolve<Service_HR_Person>())
and edit Program.cs with this code
static void Main(string[] argss)
{
Bootstrapper.WireUp();
this is work corectly
I created a web api only project with asp.net 5 and trying to set up my NHibernate session management. Everything seems to be fine but inside my controller when I try to use the session, I get a null exception although I mark it with NHibernateSession attribute.
I realized that the method does not use the session created by the attribute. What should I do to make it use that one ? Any suggestions are greatly appreciated.
public class ValuesController : Controller
{
private readonly IRepository _repository;
public ISessionContainer NHibernateSession { get; set; }
public ValuesController(IRepository repository)
{
_repository = repository;
}
// GET: api/values
[HttpGet, NHibernateSession]
public IEnumerable<string> Get()
{
return _repository.GetProducts(); // session is null exception
}
}
public class NHibernateSessionAttribute: ActionFilterAttribute
{
protected ISessionContainer sessionContainer;
public NHibernateSessionAttribute()
{
sessionContainer = new NHibernateSessionContainer();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
sessionContainer.OpenSession();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
sessionContainer.CloseSession();
}
}
public class NHibernateSessionContainer : ISessionContainer
{
public static ISessionFactory SessionFactory = CreateSessionFactory();
private ISession _session;
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(OracleClientConfiguration.Oracle10
.ConnectionString(ConfigurationManager.ConnectionStrings["Main.ConnectionString"].ConnectionString))
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.BuildConfiguration().BuildSessionFactory();
}
public ISession Session
{
get { return _session; }
}
public void OpenSession()
{
_session = SessionFactory.OpenSession();
}
public void CloseSession()
{
Dispose();
}
public void Dispose()
{
_session.Dispose();
}
}
services.AddSingleton<ISessionContainer, NHibernateSessionContainer>();
services.AddTransient<IRepository, Repository>();
session is not enabled by default, did you add it via nuget and configure it for example:
app.UseSession();
app.UseInMemorySession(configure: s => s.IdleTimeout = TimeSpan.FromMinutes(20));
see also
I am still learning UnitOfWork pattern and I am not comfortable with this yet. I found many examples but nothing is clear enough for my problem.
I want use UnitOfWork with Ado.Net.
I have many repositories. I want call different methods from different repositories in same transaction using Unit of work.
For example a have this 2 repositories.
public class FirstRepository : IFirstRepository
{
private readonly ILogger logger;
private readonly IImportConfiguration configuration;
public FirstRepository(ILogger logger, IImportConfiguration configuration)
{
this.logger = logger;
this.configuration = configuration;
}
public int Save()
{
//Save to DB with Ado.Net
return 1;
}
}
public class SecondRepository : ISecondRepository
{
private readonly ILogger logger;
private readonly IImportConfiguration configuration;
public SecondRepository(ILogger logger, IImportConfiguration configuration)
{
this.logger = logger;
this.configuration = configuration;
}
public int Update()
{
//Update in DB with Ado.Net
return 1;
}
}
I want call functions Save() and Update() in same transaction.
using (var uow = UnitOfWorkFactory.Create())
{
firstRepository.Save();
secondRepository.Update();
_unitOfWork.SaveChanges();
}
Problem is how to use same UnitOfWork in both repositories ? Only thing I can see is add additional parameter to functions
//in first repository
Save(IUnitOfWork uow)
//in second repository
Update(IUnitOfWork uow)
//****************************
using (var uow = UnitOfWorkFactory.Create())
{
firstRepository.Save(uow);
secondRepository.Update(uow);
_unitOfWork.SaveChanges();
}
This is ugly solution, because i must have this parameter in all functions that work with DB.
I am using Dependency injection. ILogger and IImportConfiguration are injected with AutoFac. Maybe would be good to register all repositories in UnitOfWork? But how to do that? I cant have just one instance injected in all repositories.
Any idea?
public class UnitOfWork
{
public DbSet<Company> Companies { get; set; }
public int SaveChanges()
{
underlyingContext.SaveChanges();
}
}
public class UnitOfWorkFactory
{
public UnitOfWork Create()
{
// real creation logic
return new UnitOfWork();
}
}
public class CompanyRepository
{
private readonly UnitOfWork uow;
public CompanyRepository(UnitOfWork uow)
{
uow = uow;
}
public void Add(Company company)
{
uow.Companies.Add(company);
}
}
public class CompanyRepositoryFactory
{
public Create(UnitOfWork uow)
{
new CompanyRepository(uow);
}
}
Tying it all together:
var uow = new UnitOfWorkFactory().Create();
var companyRepository = new CompanyRepositoryFactory().Create(uow);
So to use DI, you need to create interfaces for all these.
The unit of work is based round some Data Layer connection, for example EF uses DbContext which you would use in the underlying UnitOfWork class.
Other things you can do is make IUnitOfWork (the interface) inherit IDisposable so you use the using().
To make it so you don't have a hundred repository classes (although not really a bad thing) you can make it Generic, so IRepository<T> and Repository<T>
So for a generic repo and unit of work using EF.
public class UnitOfWork : IUnitOfWork
{
ProjectDbContext context;
public UnitOfWork() {
context = new ProjectDbContext();
}
public IQueryable<T> Query<T>(Expression<Func<bool, t>> predicate)
{
return context.Set<T>().Where(predicate);
}
public void Add<T>(T entity)
{
context.Set<T>().Add(entity);
}
public int SaveChanges()
{
return context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public class UnitOfWorkFactory
{
Lazy<UnitOfWork> lazyUOW = new Lazy<UnitOfWork>(() => new UnitOfWork());
public UnitOfWork Create()
{
// having the DI initialise as Singleton isn't enough.
return lazyUOW.Value;
}
}
public class Repository<T> : IRepository<T>
{
private readonly IUnitOfWork uow;
public Repository(IUnitOfWork uow)
{
uow = uow;
}
public void Add(T entity)
{
uow.Add(entity);
}
public List<T> AllBySomePredicate(Expression<Func<bool, T>> predicate)
{
return uow.Query(predicate).ToList();
}
}
public class RepositoryFactory : IRepositoryFactory
{
public Create<T>(UnitOfWork uow)
{
new Repistory<T>(uow);
}
}
Usage:
public class CompanyController : Controller
{
private readonly IUnitOfWorkFactory uowFactory;
private readonly IRepositoryFactory repoFactory;
public CompanyController (
IUnitOfWorkFactory uowFactory,
IRepositoryFactory repoFactory)
{
uowFactory = uowFactory;
repoFactory = repoFactory;
}
public ActionResult Index()
{
using(var uow = uowFactory.Create())
{
var companyRepo = repoFactory.Create<Company>(uow);
return View(companyRepo.AllBySomePredicate(x => x.CompanyJoined == DateTime.Now.AddMonths(-2)));
}
}
}
I am using NHibernate, and I have a BootStrapper class that has a method that returns an ISession. In my Repository class I am passing it an ISession Object. In my Controller, I am passing its constructor an IRepository object.
I have been successful in binding my IRepository to Repository class, but I can't figure out how to bind/register my Repository class so that it receives a ISession object from the BootStrapper class when it is instantiated and bind my controller to recieve a IRepository object when it is created.
My Code:
public interface IProductsRepository
{
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
}
public class MySqlProductsRepository : IProductsRepository
{
private readonly ISession _session;
public MySqlProductsRepository(ISession session)
{
_session = session;
}
public IQueryable<Product> Products
{
get
{
return _session.CreateCriteria(typeof (Product)).List<Product>().AsQueryable();
}
}
public class BootStrapper
{
public static ISessionFactory SessionFactory = CreateSessionFactory();
private static ISessionFactory CreateSessionFactory()
{
var cfg = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "nhibernate.config"));
cfg.SetProperty(NHibernate.Cfg.Environment.ConnectionStringName, System.Environment.MachineName);
NHibernateProfiler.Initialize();
return cfg.BuildSessionFactory();
}
public static ISession GetSession()
{
return SessionFactory.GetCurrentSession();
}
}
public class AdminController : Controller
{
private readonly IProductsRepository _productsRepository;
public AdminController(IProductsRepository productsRepository)
{
_productsRepository = productsRepository;
}
}
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel = new StandardKernel(new DaisyBlossomsServices());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController) _kernel.Get(controllerType);
}
public class DaisyBlossomsServices : NinjectModule
{
public override void Load()
{
Bind<IProductsRepository>().To<MySqlProductsRepository>();
}
}
}
How do I bind a ISession to MySqlProductsRepository so it receives an ISession object for its constructor, and how do I bind it so my controller receives an IProductsRepository in its constructor?
you can bind ISession to a Provider. In this case you can change the BootStrapper class to inherit from Provider and implement the CreateInstance method.
That would look like that:
public class BootStrapper : Provider<ISession>
{
.....
protected override ISession CreateInstance(IContext context)
{
return GetSession();
}
....
}
After that just add the binding to your modul:
public class DaisyBlossomsServices : NinjectModule
{
public override void Load()
{
Bind<IProductsRepository>().To<MySqlProductsRepository>();
Bind<ISession>().ToProvider<BootStrapper>();
}
}
That should do it.