我现在正在编写一个新的应用程序,虽然公司标准是使用NHibernate(因为这是所有项目的标准),而我现在使用的是ASP.NET MVC 3,因为它已经成熟。我已经在控制器中实现了我的事务(据说这是你应该这样做的方式),所以它在我的根控制器中看起来像这样:
[TransactionPerRequest]
public class FbsController : Controller
{
}
然后我的所有控制器都继承自FbsController
。这样做的原因是因为90%的所有操作都将转移到数据库,因此创建事务并将其处理剩余10%的操作(很少会执行)的开销不值得用[TransactionPerRequest]
装饰每个动作。
总是难以理解的事情是关于NHibernate会话。在存储库类中,这与我所拥有的一致,尽管这在其他项目中是不同的:
public void Add(User user)
{
using (ISession session = NHibernateHelper.OpenSession())
{
session.Save(user);
}
}
public void Remove(User user)
{
using (ISession session = NHibernateHelper.OpenSession())
{
session.Delete(user);
}
}
public User GetById(int userId)
{
using (ISession session = NHibernateHelper.OpenSession())
{
return session.QueryOver<User>()
.Where(c => c.UserID == userId)
.SingleOrDefault();
}
}
那么对于我的存储库中的大多数函数,我必须打开一个会话。有没有办法避免这种行为,所以我不必在每个存储库方法中打开一个会话?这似乎有点反直觉,因为我通常必须为每一个人做这件事。我想知道其他人的解决方案是什么对交易&amp;我看到会话问题以各种方式散布在代码中。
实际上我希望我的存储库方法如下所示:
public void Add(User user)
{
session.Save(user);
}
public void Remove(User user)
{
session.Delete(user);
}
public User GetById(int userId)
{
return session.QueryOver<User>()
.Where(c => c.UserID == userId)
.SingleOrDefault();
}
隐含处理所有事情。
答案 0 :(得分:10)
您可以查看Ayende Rahien撰写的以下系列博客文章:
答案 1 :(得分:4)
我做了一些事情:
在我的Global.asax.cs中:
public static ISessionFactory SessionFactory { get; set; }
然后在Application_Start
中定义:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
var nhConfig = new Configuration().Configure();
SessionFactory = nhConfig.BuildSessionFactory();
}
然后创建此类:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class NHSession : ActionFilterAttribute
{
public NHSession()
{
Order = 100;
}
protected ISessionFactory sessionFactory
{
get
{
return MvcApplication.SessionFactory;
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var session = sessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var session = CurrentSessionContext.Unbind(sessionFactory);
if (session != null)
{
if (session.Transaction.IsActive)
{
try
{
session.Transaction.Commit();
}
catch
{
session.Transaction.Rollback();
}
}
session.Close();
}
}
}
然后我的通用存储库看起来像是:
public class Repository<T> : IRepository<T>
{
private readonly ISessionFactory SessionFactory;
public Repository(ISessionFactory sessionFactory)
{
SessionFactory = sessionFactory;
}
public ISession Session
{
get
{
return SessionFactory.GetCurrentSession();
}
}
public T Get(long id)
{
return Session.Get<T>(id);
}
}
我对存储库的具体实现是:
public class CmsContentRepository : Repository<CmsContent>, ICmsContentRepository
{
public CmsContentRepository(ISessionFactory sessionFactory) : base(sessionFactory) { }
}
还有一件事我装饰我的控制器就像这样:
[NHSession]
public ViewResult Revisions(int id)
{
var model = Service.CmsContentRepository.Get(id);
return View("Revisions", model);
}
这使我能够在请求中使用一个工作单元。基本上是请求进入并启动会话,SessionFactory
被传递到存储库的构造函数中。我在这里使用DI,但这是可选的。如果检测到错误,则会话将回滚,如果没有在请求结束时提交。我会推荐NHProf,因为它可以帮助您理解会话管理(即如果没有正确设置)。
答案 2 :(得分:0)
我使用StructureMap在首次调用ISession时自动启动会话,然后通过HttpRequest缓存会话。这使我可以在整个请求中使用延迟加载和事务,而且编写文件最少。
这是我的引导程序的代码,它使用Fluent NHibernate和StructureMap为我设置了一切。
public class Bootstrapper
{
public static ISessionFactory DBSessionFactory { get; set; }
public static ISession DBSession { get; set; }
public static void InitializeObjectFactory()
{
ObjectFactory.Initialize(x =>
{
x.PullConfigurationFromAppConfig = true;
x.Scan(y =>
{
y.Assembly(Assembly.GetAssembly(typeof(AccountController)));
y.Assembly(Assembly.GetAssembly(typeof(IMyProject)));
y.WithDefaultConventions();
}
);
// these are for NHibernate
x.ForRequestedType<ISessionFactory>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.ConstructedBy(GetDBSessionFactory);
// open session at beginning of every http request
// (the session is disposed at end of http request in global.asax's Application_EndRequest)
x.ForRequestedType<ISession>()
.CacheBy(InstanceScope.HttpContext)
.TheDefault.Is.ConstructedBy(GetDBSession);
});
}
public static ISessionFactory CreateSessionFactory()
{
return GetFluentConfiguration()
.BuildSessionFactory();
}
public static ISessionFactory GetDBSessionFactory()
{
if (DBSessionFactory == null)
{
DBSessionFactory = CreateSessionFactory();
}
return DBSessionFactory;
}
public static ISession GetDBSession()
{
if (DBSession == null)
{
DBSession = CreateSession();
}
return DBSession;
}
public static ISession CreateSession()
{
return GetDBSessionFactory()
.OpenSession();
}
public static FluentConfiguration GetFluentConfiguration()
{
string commandTimeout = ConfigurationManager.AppSettings["MyDBCommandTimeout"];
return Fluently.Configure()
.Database(// use your db configuration )
.Mappings(m =>
{
m.HbmMappings
.AddFromAssemblyOf<MyEO>();
m.FluentMappings
.AddFromAssemblyOf<MyEO>()
.AddFromAssemblyOf<MyEOMap>();
})
.ExposeConfiguration(
cfg =>
{
// command_timeout sets the timeout for the queries
cfg.SetProperty("command_timeout", commandTimeout);
}
);
}
}
调用Bootstrapper.InitializeObjectFactory();在global.asax的Application_Start()方法中,如下所示:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
Bootstrapper.InitializeObjectFactory();
...
}
关闭Application_EndRequest()中的会话:
protected void Application_EndRequest()
{
// ensure that we aren’t leaking ISessions on every web request
if (Bootstrapper.DBSession != null)
{
if (Bootstrapper.DBSession.IsOpen)
{
Bootstrapper.DBSession.Close();
}
Bootstrapper.DBSession.Dispose();
Bootstrapper.DBSession = null;
}
HttpContextBuildPolicy.DisposeAndClearAll();
}
现在你打电话
ObjectFactory.GetInstance<ISession>()
从任何地方(我将它包装在一个帮助器类中以保持我的代码简单),StructureMap将为您提供缓存的会话。