我应该如何将我的 select 语句包装在事务中?
我要用 nhibernate profiler 扔掉我的网站,我收到了这条消息
警告:隐式事务的使用是 气馁
http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions
我看到它们在每一个单据上选择语句。
private readonly ISession session;
public OrderHistoryRepo(ISession session)
{
this.session = session;
}
public void Save(OrderHistory orderHistory)
{
session.Save(orderHistory);
}
public List<OrderHistory> GetOrderHistory(Guid Id)
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
public void Commit()
{
using (ITransaction transaction = session.BeginTransaction())
{
transaction.Commit();
}
}
我应该像我的提交那样用事务包装我的 GetOrderHistory 吗?
编辑
我如何将 select 语句与事务一起包装?会是这样吗?但“交易”永远不会被使用。
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
}
编辑
Ninject(也许我可以利用它来帮助我,就像我在获得会话时所做的那样)
public class NhibernateSessionFactory
{
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Map>().Conventions.Add(ForeignKey.EndsWith("Id")))
.ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
//.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();
}
}
编辑3
如果我这样做,
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
}
我会得到这个alert
如果我这样做,
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList().ConvertToLocalTime(timezoneId);
transaction.Commit();
return orderHistories;
}
}
我可以消除错误,但会得到意想不到的结果。
例如,当我返回 orderHistories 时,我会循环遍历所有这些并将“购买日期”转换为用户当地时间。这是通过我为列表创建的扩展方法来完成的。
转换后,我将其设置为覆盖对象中的“购买日期”。这样我就不必为字段的一次更改创建新对象。
现在,如果我在调用提交之前进行日期转换,nhibernate 认为我已经更新了对象并且需要提交它。
所以我对这个问题悬赏。
- 如何创建我的方法,以便不必将每个方法包装在事务中?我已经在我的会话中使用 ninject,所以也许我可以利用它,但是有时我被迫在单个请求中执行多个事务。
所以我不知道每个请求只有一笔交易是一个灵魂。
如何确保我为临时使用而更改的对象不会意外得到提交?
如何在服务层中使用延迟加载。我不想将我的延迟加载内容包含在事务中,因为它通常在我的服务层中使用。
我发现很难找到使用存储库模式时如何执行此操作的示例。在示例中,所有内容始终都写在同一个事务中,我不想在我的服务层中进行事务(这是存储库的工作,而不是我的业务逻辑)
I am going threw my site with nhibernate profiler and I got this message
Alert: Use of implicit transactions is
discouraged
http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions
I see they are on every single select statement.
private readonly ISession session;
public OrderHistoryRepo(ISession session)
{
this.session = session;
}
public void Save(OrderHistory orderHistory)
{
session.Save(orderHistory);
}
public List<OrderHistory> GetOrderHistory(Guid Id)
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
public void Commit()
{
using (ITransaction transaction = session.BeginTransaction())
{
transaction.Commit();
}
}
Should I be wrapping my GetOrderHistory with a transaction like I have with my commit?
Edit
How would I wrap select statements around with a transaction? Would it be like this? But then "transaction" is never used.
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
}
Edit
Ninject (maybe I can leverage it to help me out like I did with getting a session)
public class NhibernateSessionFactory
{
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Map>().Conventions.Add(ForeignKey.EndsWith("Id")))
.ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
//.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();
}
}
Edit 3
If I do this
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
return orderHistories;
}
}
I get this alert
If I do this
public List<OrderHistory> GetOrderHistory(Guid Id)
{
using (ITransaction transaction = session.BeginTransaction())
{
List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList().ConvertToLocalTime(timezoneId);
transaction.Commit();
return orderHistories;
}
}
I can get rid of the errors but can get unexpected results.
For instance when I get orderHistories back I loop through all of them and convert the "purchase date" to the users local time. This is done through an extension method that I created for my list.
Once converted I set it to override the "purchase date" in the object. This way I don't have to create a new object for one change of a field.
Now if I do this conversion of dates before I call the commit nhibernate thinks I have updated the object and need to commit it.
So I am putting a bounty on this question.
- How can I create my methods so I don't have to wrap each method in a transaction? I am using ninject already for my sessions so maybe I can leverage that however some times though I am forced to do multiple transactions in a single request.
So I don't know have just one transaction per request is a soultion.
how can I make sure that objects that I am changing for temporary use don't accidentally get commit?
how can I have lazy loading that I am using in my service layer. I don't want to surround my lazy loading stuff in a transaction since it usually used in my service layer.
I am finding it very hard to find examples of how to do it when your using the repository pattern. With the examples everything is always written in the same transaction and I don't want to have transactions in my service layer(it is the job of the repo not my business logic)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
NHibernate 社区建议您将所有内容包装在事务中,无论您在做什么。
要回答你的第二个问题,一般来说,这取决于。如果这是一个 Web 应用程序,您应该查看每个请求的会话模式。在大多数基本场景中,这意味着您将为每个 HTTP 请求创建一个会话,其中在发出请求并在请求结束时提交/处置时创建会话(和事务)。我并不是说这对您来说是正确的方法,但这是一种对大多数人都有效的常见方法。
有很多例子展示了如何做到这一点。绝对值得花时间进行搜索和阅读。
编辑:我如何执行每个请求的会话/事务的示例:
我有一个 SessionModule,它从我的依赖解析器加载会话(这是 MVC3 功能):
这就是我注册会话的方式(使用StructureMap):
请记住,这是我已经尝试过的东西,并且发现它非常适合我使用 NHibernate 的场景。其他人可能有不同的意见(当然,欢迎)。
The NHibernate community recommends that you wrap everything in a transaction, regardless of what you're doing.
To answer your second question, generally, it depends. If this is a web application, you should look at the session-per-request pattern. In most basic scenarios, what this means is that you'll create a single session per HTTP request in which the session (and transaction) is created when the request is made and committed/disposed of at the end of the request. I'm not saying that this is the right way for you, but it's a common approach that works well for most people.
There are a lot of examples out there showing how this can be done. Definitely worth taking the time to do a search and read through things.
EDIT: Example of how I do the session/transaction per request:
I have a SessionModule that loads the session from my dependency resolver (this is a MVC3 feature):
This is how I register the session (using StructureMap):
Keep in mind that this is something I've experimented with and have found to work well for the scenarios where I've used NHibernate. Other people may have different opinions (which are, of course, welcome).
好吧,我想您可以设置适合您在应用程序中执行的读取类型的事务级别,但问题是:真的需要在应用程序代码中执行此操作吗?我的猜测是否定的,除非您的用例与 (n)hibernate 将通过配置应用的默认事务级别不同。
也许您可以在 nhibernate 配置中设置事务级别。
或者也许分析器的设置有点过于热心?从这里看不出来。
但是:您是否尝试过提交读取事务?不应该造成任何伤害。
Well, i guess you could set a Transaction level that's appropriate for the kind of reads that you perform in your application, but the question is: should it really be required to do that within the application code? My guess is no, unless you have a use case that differs from the default transaction levels that (n)hibernate will apply by configuration.
Maybe you can set transaction levels in your nhibernate config.
Or maybe the settings for the profiler are a bit overzealous? Can't tell from here.
But: Have you tried commiting a read transaction? Should not do any harm.
您将 ISession 传递到存储库的构造函数中,这很好。但这是我对这门课唯一喜欢的地方。
session.Get(id)
来实现此目的。如果需要,您可以将结果放入集合中。直接回答你的问题...
我推荐的模式如下。这使用手动依赖项注入,但您可以使用 Ninject 来解决您的依赖项。
在一个会话中进行多个事务是完全可以的。我不喜欢等到请求结束才提交,因为为用户提供良好的反馈为时已晚。
唯一可靠的方法是使用 IStatelessSession。不太确定的方法是从会话中逐出对象或清除会话。但对于 NHibernate,不建议修改持久对象。
如果您使用每个请求会话,这应该不是问题。但你是对的,延迟加载可能发生在事务之外。我忽略这些警告。我想您可以“触摸”所需的每个子对象,以便延迟加载处于事务中,但我不打扰。
我不同意这一点。 UI 或业务逻辑应该管理事务。 UI 是用户表达其意图(保存或取消我的更改)的地方,也是管理事务的自然位置。
You're passing the ISession into the repository's constructor, which is good. But that's the only thing I like about this class.
session.Get<OrderHistory>(id)
for this. You can put the result into a collection if needed.To answer your questions directly...
The pattern I recommend is below. This uses manual dependency injection but you could use Ninject to resolve your dependencies.
It's perfectly fine to have multiple transactions in a session. I don't like waiting until the request ends to commit because it's too late to provide good feedback to the user.
The only sure way is to use an IStatelessSession. Less sure ways are to Evict the object from the Session or Clear the session. But with NHibernate it's not recommended to modify persistent objects.
If you're using session-per-request this shouldn't be a problem. But you're right that lazy-loading can happen outside of the transaction. I ignore these warnings. I suppose you could "touch" every child object needed so that lazy loads are in a transaction but I don't bother.
I disagree with this. The UI or business logic should manage the transaction. The UI is where the user expresses their intent (save or cancel my changes) and is the natural place to manage the transaction.
(会话+交易)每个请求。
当然你可以使用 NInject 来管理
会话生命周期,我 博客
最近关于类似的方法
使用温莎城堡。
这里有 4 个选项:
做临时改变
我会选择第一个。
(session+transaction) per request.
Sure you can use NInject to manage
session lifecycle, I blogged
recently about similar approach
using Castle Windsor.
Here are 4 options:
do temporary change
I'd go with first one.