我应该如何将我的 select 语句包装在事务中?

发布于 2024-11-09 14:29:35 字数 4605 浏览 1 评论 0原文

我要用 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 认为我已经更新了对象并且需要提交它。

所以我对这个问题悬赏。

  1. 如何创建我的方法,以便不必将每个方法包装在事务中?我已经在我的会话中使用 ninject,所以也许我可以利用它,但是有时我被迫在单个请求中执行多个事务。

所以我不知道每个请求只有一笔交易是一个灵魂。

  1. 如何确保我为临时使用而更改的对象不会意外得到提交?

  2. 如何在服务层中使用延迟加载。我不想将我的延迟加载内容包含在事务中,因为它通常在我的服务层中使用。

我发现很难找到使用存储库模式时如何执行此操作的示例。在示例中,所有内容始终都写在同一个事务中,我不想在我的服务层中进行事务(这是存储库的工作,而不是我的业务逻辑)

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.

  1. 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.

  1. how can I make sure that objects that I am changing for temporary use don't accidentally get commit?

  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

等风来 2024-11-16 14:29:35

NHibernate 社区建议您将所有内容包装在事务中,无论您在做什么。

要回答你的第二个问题,一般来说,这取决于。如果这是一个 Web 应用程序,您应该查看每个请求的会话模式。在大多数基本场景中,这意味着您将为每个 HTTP 请求创建一个会话,其中在发出请求并在请求结束时提交/处置时创建会话(和事务)。我并不是说这对您来说是正确的方法,但这是一种对大多数人都有效的常见方法。

有很多例子展示了如何做到这一点。绝对值得花时间进行搜索和阅读。


编辑:我如何执行每个请求的会话/事务的示例:

我有一个 SessionModule,它从我的依赖解析器加载会话(这是 MVC3 功能):

namespace My.Web
{
    public class SessionModule : IHttpModule {
        public void Init(HttpApplication context) {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        void context_BeginRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Begin();
        }

        void context_EndRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Commit();
            session.Dispose(); 
        }

        public void Dispose() {}
    }
}

这就是我注册会话的方式(使用StructureMap):

new Container(x => {

    x.Scan(a => {
        a.AssembliesFromApplicationBaseDirectory();
        a.WithDefaultConventions();
    });

    x.For<ISessionFactory>().Singleton().Use(...);
    x.For<ISession>().HybridHttpOrThreadLocalScoped().Use(sf => sf.GetInstance<ISessionFactory>().OpenSession());
    x.For<StringArrayType>().Use<StringArrayType>();

});

请记住,这是我已经尝试过的东西,并且发现它非常适合我使用 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):

namespace My.Web
{
    public class SessionModule : IHttpModule {
        public void Init(HttpApplication context) {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        void context_BeginRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Begin();
        }

        void context_EndRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Commit();
            session.Dispose(); 
        }

        public void Dispose() {}
    }
}

This is how I register the session (using StructureMap):

new Container(x => {

    x.Scan(a => {
        a.AssembliesFromApplicationBaseDirectory();
        a.WithDefaultConventions();
    });

    x.For<ISessionFactory>().Singleton().Use(...);
    x.For<ISession>().HybridHttpOrThreadLocalScoped().Use(sf => sf.GetInstance<ISessionFactory>().OpenSession());
    x.For<StringArrayType>().Use<StringArrayType>();

});

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).

狠疯拽 2024-11-16 14:29:35

好吧,我想您可以设置适合您在应用程序中执行的读取类型的事务级别,但问题是:真的需要在应用程序代码中执行此操作吗?我的猜测是否定的,除非您的用例与 (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.

ゃ懵逼小萝莉 2024-11-16 14:29:35

您将 ISession 传递到存储库的构造函数中,这很好。但这是我对这门课唯一喜欢的地方。

  • Save只是调用session.Save,所以不需要。
  • GetOrderHistory 似乎是通过 ID 检索单个实体,您应该使用 session.Get(id) 来实现此目的。如果需要,您可以将结果放入集合中。
  • Commit 方法不应该位于存储库中。

直接回答你的问题...

如何创建我的方法,这样我就不必将每个方法包装在
交易?我正在使用 ninject
已经开始我的课程了,所以也许我可以
然而有时会利用这一点
虽然我被迫做多个
单个请求中的事务。

我推荐的模式如下。这使用手动依赖项注入,但您可以使用 Ninject 来解决您的依赖项。

List<OrderHistory> orderHistories;
var session = GetSession(); // Gets the active session for the request
var repository = new OrderHistory(Repository);
// new up more repositories as needed, they will all participate in the same transaction
using (var txn = session.BeginTransaction())
{
    // use try..catch block if desired
    orderHistories = repository.GetOrderHistories();
    txn.Commit();
}

所以我不知道只有一个
每个请求的交易是一个灵魂。

在一个会话中进行多个事务是完全可以的。我不喜欢等到请求结束才提交,因为为用户提供良好的反馈为时已晚。

我怎样才能确保对象
我是临时用的,不用
意外得到提交?

唯一可靠的方法是使用 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.

  • Save just calls session.Save, so it's not needed.
  • GetOrderHistory appears to be retrieving a single entity by ID, you should use session.Get<OrderHistory>(id) for this. You can put the result into a collection if needed.
  • The Commit method shouldn't be in a repository.

To answer your questions directly...

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.

The pattern I recommend is below. This uses manual dependency injection but you could use Ninject to resolve your dependencies.

List<OrderHistory> orderHistories;
var session = GetSession(); // Gets the active session for the request
var repository = new OrderHistory(Repository);
// new up more repositories as needed, they will all participate in the same transaction
using (var txn = session.BeginTransaction())
{
    // use try..catch block if desired
    orderHistories = repository.GetOrderHistories();
    txn.Commit();
}

So I don't know have just one
transaction per request is a soultion.

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.

how can I make sure that objects that
I am changing for temporary use don't
accidentally get commit?

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.

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.

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 don't want to have transactions in
my service layer(it is the job of the
repo not my business logic)

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.

以酷 2024-11-16 14:29:35
  1. 推荐的方法是工作单元
    (会话+交易)每个请求。
    当然你可以使用 NInject 来管理
    会话生命周期,我 博客
    最近关于类似的方法
    使用温莎城堡。
  2. 这里有 4 个选项:

    • 不要临时更改实体
    • 在这种情况下使用无状态会话
    • 在需要时分离对象
      做临时改变
    • 回滚事务

    我会选择第一个。

  3. 如果您使用每个请求会话模式,则不必担心延迟加载 - 它将在同一请求中执行并自动与事务包装在一起。
  1. Recommended approach is unit of work
    (session+transaction) per request.
    Sure you can use NInject to manage
    session lifecycle, I blogged
    recently about similar approach
    using Castle Windsor.
  2. Here are 4 options:

    • Don't change entities temporary
    • Use stateless session in such cases
    • Detach objects when you are going to
      do temporary change
    • Rollback transaction

    I'd go with first one.

  3. You don't have to worry about lazy loading if you are using session-per-request pattern - it will be executed in same request and wrapped with transaction automatically.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文