Нужен простой пример использования nhibernate + единицы работы + шаблон репозитория + уровень сервиса + ninject
Я использую
- nhibernate + беглое nhibernate
- asp.net mvc 3
- Ninject
В настоящее время я использую nhibernate, ninject с шаблоном репозитория и слоями сервиса.
Итак, у меня есть этот
Ninject
public class NhibernateSessionFactory
{
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Framework.Data.Mapping.TableAMap>().Conventions.Add(ForeignKey.EndsWith("Id")))
.ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
.ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"))
//.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
return fluentConfiguration;
}
private static void BuidSchema(NHibernate.Cfg.Configuration config)
{
new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
}
public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
var sessionFactory = new NhibernateSessionFactory();
return sessionFactory.GetSessionFactory();
}
}
public class NhibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope()
.OnActivation(StartTransaction)
.OnDeactivation(CommitTransaction);
}
public void CommitTransaction(ISession session)
{
if (session.Transaction.IsActive)
{
session.Transaction.Commit();
}
}
public void StartTransaction(ISession session)
{
if (!session.Transaction.IsActive)
{
session.BeginTransaction();
}
}
}
Итак, я делаю сеанс nhibernate factory один раз для жизни приложения, а затем я использую его, чтобы дать мне сеансы, когда мне это нужно.
Один Начало транзакции Я начинаю транзакцию, и в конце я закрываю транзакцию.
Причина, по которой я это делал, заключалась в том, что когда я использовал профилировщик nhibernate, я бы получил множество предупреждений об использовании неявных транзакций Этот вид о том, чтобы поставить бандаж на проблему, но никогда не исправлял ее (она сократила количество, но что-то ленивое загрузилось, все еще получило эту проблему).
пример repo
public class CalendarRepo : ICalendarRepo
{
private readonly ISession session;
public CalendarRepo(ISession session)
{
this.session = session;
}
public List<CalendarAppointment> RepeatingAppointments(int repeatingId)
{
List<CalendarAppointment> calendarAppointments = session.Query<CalendarAppointment>().Where(x => x.RepeatingId == repeatingId && x.RepeatingId != 0)
.Take(QueryLimits.Appointments)
.ToList();
return calendarAppointments;
}
}
уровень обслуживания
public class CalendarService : ICalendarService
{
private readonly ICalendarRepo calendarRepo;
public CalendarService(ICalendarRepo calendarRepo)
{
this.calendarRepo = calendarRepo;
}
// normally would return something and take in params
public void SampleServiceMethod()
{
// do some checks if needed
// call up the repository
// call commit
// done.
}
}
Итак, это в основном то, что у меня есть.
Я хотел бы использовать шаблон работы, чтобы получить больше вещей, совершающих сделки, и делать что-то правильно с транзакциями (так как сейчас мне сказали, что я делаю их не совсем право)
Итак, я ищу простой пример того, как заставить их всех работать вместе и выяснить, насколько мне нужно изменить то, что я получил до сих пор.
Большинство учебных пособий, которые я видел, более сложны, чем хотелось бы. Большинство из них делают TDD и делают общие репозитории хорошими, но прежде чем я доберусь до этого этапа, я бы хотел увидеть учебник, который просто делает это, даже если он повторяет код.
Edit
Итак, я играл с ним и придумал этот очень простой пример. Я не уверен, правильно ли я делаю это.
Ninject
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
kernel.Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
kernel.Bind<ITable1Repo>().To<Table1Repo>();
kernel.Bind<ITable1Service>().To<Table1Service>();
kernel.Bind<IUnitofWork>().To<UnitofWork>();
}
Класс nhibernate factory показан в оригинальной записи.
// Controller
public class Default1Controller : Controller
{
private readonly ITable1Service table1Service;
//
// GET: /Default1/
public Default1Controller(ITable1Service table1Service)
{
this.table1Service = table1Service;
}
public ActionResult Index()
{
table1Service.Save();
return View();
}
}
//domain
public class Table1
{
public virtual int Id { get; private set; }
public virtual string C1 { get; set; }
public virtual string C2 { get; set; }
}
//Свободное сопоставление
public class Table1Mapping : ClassMap<Table1>
{
public Table1Mapping()
{
Id(x => x.Id);
Map(x => x.C1);
Map(x => x.C2);
}
}
//Repo
public class Table1Repo : unitofwork.Models.Repository.ITable1Repo
{
private readonly ISession session;
public Table1Repo(ISession session)
{
this.session = session;
}
public void Create(Table1 tbl1)
{
session.Save(tbl1);
}
}
//сервисный уровень
public class Table1Service : unitofwork.Models.Service.ITable1Service
{
private readonly ITable1Repo table1Repo;
private readonly IUnitofWork unitOfWork;
public Table1Service(ITable1Repo table1Repo, IUnitofWork unitOfWork)
{
this.table1Repo = table1Repo;
this.unitOfWork = unitOfWork;
}
public void Save()
{
Table1 a = new Table1();
a.C1 = "test";
a.C2 = "test2";
table1Repo.Create(a);
unitOfWork.Commit();
}
}
//Единица работы
public class UnitofWork : unitofwork.Models.IUnitofWork
{
private readonly ITransaction transaction;
private readonly ISession session;
public UnitofWork(ISession session)
{
this.session = session;
session.FlushMode = FlushMode.Auto;
transaction = session.BeginTransaction(IsolationLevel.ReadCommitted);
}
public void Commit()
{
if (!transaction.IsActive)
{
throw new InvalidOperationException("Oops! We don't have an active transaction");
}
transaction.Commit();
}
public void Rollback()
{
if (transaction.IsActive)
{
transaction.Rollback();
}
}
public void Dispose()
{
if (session.IsOpen)
{
session.Close();
}
}
}
Ответы
Ответ 1
Я использую 'vanilla' ASP.NET, а не ASP.NET MVC 3, но по существу мы делаем то же самое.
Во-первых, у меня есть отдельный класс UnitOfWork
:
public class UnitOfWork
{
private static ISessionFactory SessionFactory
{
get
{
return Container.Get<ISessionFactory>();
}
}
public static ISession Session
{
get
{
return SessionFactory.GetCurrentSession();
}
}
public static void Start()
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
Session.FlushMode = FlushMode.Commit;
Session.BeginTransaction(IsolationLevel.ReadCommitted);
}
public static void Rollback()
{
Rollback(true);
}
/// <summary>
/// Rollback the current transaction, and optionally start a new transaction
/// </summary>
/// <param name="startNew">Whether to start a new transaction and keep the session open</param>
public static void Rollback(bool startNew)
{
ISession session = CurrentSessionContext.Unbind(SessionFactory);
if (session != null)
{
// Rollback current transaction
if (session.Transaction.IsActive && !session.Transaction.WasRolledBack)
{
session.Transaction.Rollback();
}
// Close and discard the current session
session.Close();
session.Dispose();
session = null;
}
if (startNew)
{
Start();
}
}
/// <summary>
/// Commit the current transaction, keeping the current session open and starting a new transaction
///
/// Call Commit multiple times during a single unit of work if you want to commit database changes in
/// multiple transactions
/// </summary>
public static void Commit()
{
Commit(true);
}
/// <summary>
/// Commit the current transaction, and optionally keep the session open and start a new transaction
///
/// Call Commit multiple times during a single unit of work if you want to commit database changes in
/// multiple transactions
/// </summary>
/// <param name="startNew">Whether to start a new transaction and keep the session open</param>
public static void Commit(bool startNew)
{
if (startNew)
{
Session.Transaction.Commit();
Session.BeginTransaction(IsolationLevel.ReadCommitted);
}
else
{
ISession session = CurrentSessionContext.Unbind(SessionFactory);
if (session != null)
{
if (session.Transaction.IsActive && !session.Transaction.WasRolledBack)
{
session.Transaction.Commit();
}
session.Close();
session.Dispose();
session = null;
}
}
}
}
Я использую HTTP-модуль для запуска новой единицы работы для каждого веб-запроса и автоматического фиксации/отката. Не уверен, нужен ли вам HTTP-модуль при использовании ASP.NET MVC 3, или если есть какой-то другой способ сделать это. В любом случае, соответствующие части, показанные ниже:
public class IoCHttpModule : IHttpModule, IDisposable
{
private HttpApplication httpApplication;
public void Init(HttpApplication context)
{
if (context == null)
throw new ArgumentException("context");
this.httpApplication = context;
this.httpApplication.BeginRequest += new EventHandler(BeginRequest);
this.httpApplication.EndRequest += new EventHandler(EndRequest);
this.httpApplication.Error += new EventHandler(Error);
StandardIoCSetup.Initialise(SessionContextType.Web);
}
private void BeginRequest(object sender, EventArgs e)
{
UnitOfWork.Start();
}
private void EndRequest(object sender, EventArgs e)
{
UnitOfWork.Commit(false);
}
private void Error(object sender, EventArgs e)
{
UnitOfWork.Rollback(false);
}
public void Dispose()
{
if (this.httpApplication == null)
return;
this.httpApplication.Dispose();
}
}
Итак, для каждого веб-запроса запускается новая единица работы и автоматически фиксируется, если нет необработанных исключений. Конечно, вы можете вручную вызвать UnitOfWork.Commit()
или UnitOfWork.Rollback()
в рамках веб-запроса, если это необходимо. Строка StandardIoCSetup.Initialise...
настраивает NHibernate с использованием модуля Ninject, почти так же, как вы уже делаете.
Таким образом, по сути, это не так много, чтобы добавить единицу работы к тому, что у вас уже есть.