Нужен простой пример использования nhibernate + unit of work + repository pattern + service layer + 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 session factory один раз в течение всего срока службы приложения, затем я использую его, чтобы дать мне сеансы, когда мне нужно он.

один старт транзакции я начинаю транзакцию и в конце закрываю транзакцию.

причина, по которой я это сделал, заключалась в том, что когда я использовал профилировщик nhibernate, я получал много предупреждений об использовании неявные транзакции этот вид поставил бандаж на проблему, но никогда не исправил ее (он сократил номер, но все, что лениво загружено, все еще имеет эту проблему).

пример РЕПО

 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 и делают общие репозитории хорошими, но прежде чем я доберусь до этого этапа, я хотел бы увидеть учебник, который делает это просто, даже если он повторяет код.

редактировать


поэтому я играл с ним и придумал этот очень простой пример. Я не уверен, что делаю это. право.

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 показан в исходном сообщении.

 // 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();
        }

    }

// домен

  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);
        }
    }

//РЕПО

 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, почти так же, как вы уже делаете.

таким образом, по сути, это не так много работы, чтобы добавить единицу работы к тому, что у вас уже есть.