2

我要用 nhibernate profiler 扔掉我的网站,我收到了这条消息

警告:不鼓励使用隐式事务

http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

我看到它们出现在每一个 select 语句中。

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?

编辑

我如何将选择语句与事务包装在一起?会是这样吗?但是从不使用“事务”。

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

我收到此警报

如果我这样做

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

我可以摆脱错误,但可以得到意想不到的结果。

例如,当我返回 orderHistory 时,我会遍历所有这些并将“购买日期”转换为用户本地时间。这是通过我为列表创建的扩展方法完成的。

转换后,我将其设置为覆盖对象中的“购买日期”。这样,我不必为字段的一次更改创建新对象。

现在,如果我在调用提交之前进行日期转换,nhibernate 认为我已经更新了对象并需要提交它。

所以我在这个问题上悬赏。

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

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

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

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

当您使用存储库模式时,我发现很难找到如何做到这一点的示例。有了这些例子,一切都写在同一个事务中,我不想在我的服务层有事务(这是回购的工作而不是我的业务逻辑)

4

4 回答 4

4

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

要回答你的第二个问题,一般来说,这取决于。如果这是一个 Web 应用程序,您应该查看 session-per-request 模式。在大多数基本场景中,这意味着您将为每个 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 的场景中效果很好。其他人可能有不同的意见(当然,这是受欢迎的)。

于 2011-05-24T00:03:49.497 回答
2

好吧,我想您可以设置一个适合您在应用程序中执行的读取类型的事务级别,但问题是:真的需要在应用程序代码中执行此操作吗?我的猜测是否定的,除非您的用例与 (n)hibernate 将通过配置应用的默认事务级别不同。

也许您可以在您的休眠配置中设置事务级别。

或者也许探查器的设置有点过分热心?从这里说不出来。

但是:您是否尝试过提交读取事务?不应该造成任何伤害。

于 2011-05-24T00:59:50.400 回答
2

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

  • Save 只是调用 session.Save,所以不需要它。
  • GetOrderHistory 似乎正在按 ID 检索单个实体,您应该使用session.Get<OrderHistory>(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 是用户表达意图(保存或取消我的更改)的地方,也是管理事务的自然场所。

于 2011-06-09T21:01:57.963 回答
0
  1. 推荐的方法是每个请求的工作单元(会话+事务)。当然,您可以使用 NInject 来管理会话生命周期,我最近在博客 中介绍了使用 Castle Windsor 的类似方法。
  2. 这里有 4 个选项:

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

    我会选择第一个。

  3. 如果您使用每请求会话模式,则不必担心延迟加载 - 它将在同一个请求中执行并自动包装事务。
于 2011-06-09T19:53:01.490 回答