【问题标题】:StructureMap returns a disposed nHibenrate session object from thread local scopeStructureMap 从线程本地范围返回已处理的 nHibenrate 会话对象
【发布时间】:2010-07-03 01:43:20
【问题描述】:

[OR] 如何为 UoW 定义一个 StructureMap 生命周期以供 http 请求和石英作业使用

我有这个使用 SM 进行 IoC 的 Web 应用程序。我正在使用 HybridHttpOrThreadLocalScoped 范围来存储我的 nHibernate ISession 对象。对于我的 Web 请求,这在每个请求的会话中都可以正常工作。

但我也有可以安排几个工作的quartz.net。该作业使用相同的工作单元来获取 ISession 对象。在这种情况下,当调度程序启动作业时,一开始一切正常,并且作业运行良好几次,直到作业线程 ID 重复。

想象一下,当作业被调度时,它开始在 ID 为 11、12、13 的线程中运行,然后再次以线程 ID 11 运行。此时结构映射返回一个已被释放的会话对象,我得到“System.ObjectDisposedException:会话已关闭!”错误。

因此,据我所知,会话保存在线程本地存储中,并且在我在工作单元结束时处理会话之后,会话对象仍保存在线程本地存储中。 似乎在线程终止后其本地存储没有被清除,并且当创建具有相同 id 的新线程时,结构映射在旧线程本地存储中查找会话(应该是为我相信的新线程清除)并返回已经释放的会话对象。

问题:

  1. 有没有办法清除线程本地存储(在终止时)?
  2. 对于线程范围的对象,是否有等效的“ReleaseAndDisposeAllHttpScopedObjects”?
  3. 有没有办法使已处理的对象无效(或弹出),这样即使 SM 查找它也找不到任何对象并必须创建一个新实例?

我希望我的问题说清楚了。这花了我几个小时的时间,但我仍然没有找到解决方法。 感谢任何提示 :>

更新: 我添加了自己的解决方案,以使 StructureMap 提供的 UoW 可以同时处理 http 请求和石英作业。如果您有更好/更简单/更简单的解决方案,请告诉我。

【问题讨论】:

  • 您是否使用 StructureMap 管理您的石英 IJobs?
  • @Mauricio:我在我的应用程序中使用 StructureMap。不过,我不确定您通过 StructeMap 管理石英作业是什么意思:>
  • 您的 Quartz IJob 实例是否由 StructureMap 管理?换句话说:你在容器中注册你的工作吗?
  • @Mauricio:不,我没有在 StructureMap 容器中注册我的工作。我正在使用 StructureMap 为每个被解雇的工作获取 UoW。
  • 我会让容器管理这些工作......它可能会让事情变得更容易。

标签: .net multithreading nhibernate structuremap quartz.net


【解决方案1】:

我正在重新审视我为使 StructureMap 与每个 Http 的 UoW 和每个石英作业的 UoW 一起工作所做的工作,我决定在这里分享我的解决方案。

所以我的想法是,我想在有 http 上下文时使用 StructureMap Hybrid 范围来获取 UoW 的实例,并且在没有 http 上下文时(例如触发石英作业时)还为每个线程获取不同的 UoW 实例)。像这样:

For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<UnitOfWork>();

http 的 UoW 运行良好。问题是每个线程的 UoW。

这就是发生的事情。当 quratz 作业触发时,它会从线程池中拉出一个线程并开始使用该线程执行作业。当工作开始时,我要求 UoW。 StructureMap 在本地存储下查找该线程以返回 UoW,但因为它找不到任何它实例化一个并将其保存在线程的本地存储下。我得到了 UoW,然后执行 Begin、Commit、Dispose,一切都很好。

当从线程池中拉出线程时会出现问题,该线程池之前用于触发作业(并使用 UoW)。在这里,当您请求 UoW 时,StructureMap 会在缓存(线程本地存储)中查找并找到 UoW 并将其返回给您。但问题是 UoW 被处理掉了!

因此,我们不能真正将每个线程的 UoW 用于石英作业,因为线程本身没有被释放,并且它们保存旧的缓存释放的 UoW。 线程的生命周期基本上与石英作业的生命周期不匹配。这就是我为石英作业创建自己的生命周期的原因。

首先我创建了自己的 http-quartz 混合生命周期类:

public class HybridHttpQuartzLifecycle : HttpLifecycleBase<HttpContextLifecycle, QuartzLifecycle>
{
    public override string Scope { get { return "HybridHttpQuartzLifecycle"; } }
}

然后我创建了我的 QuartzLifecyle 类:

public class QuartzLifecycle : ILifecycle
{

    public void EjectAll()
    {
        FindCache().DisposeAndClear();
    }

    public IObjectCache FindCache()
    {
        return QuartzContext.Cache;
    }

    public string Scope { get { return "QuartzLifecycle"; } }
}

然后我需要为 Quartz 创建一些上下文类,例如 HttpContext 来保存上下文相关信息。所以我创建了 QuartzContext 类。 当触发一个quartz 作业时,该作业的JobExecutionContext 应该在QuartzContext 中注册。然后将在特定的 JobExecutionContext 下创建 StructureMap 实例的实际缓存 (MainObjectCache)。所以这样在作业执行完成后缓存也会消失,我们就不会有在缓存中处理 UoW 的问题。

此外,由于 _jobExecutionContext 是 ThreadStatic,当我们从 QuartzContext 请求缓存时,它会返回为同一线程保存的 JobExecutionContext 中的缓存。因此,当多个作业同时运行时,它们的 JobExecutionContexts 会单独保存,我们将为每个正在运行的作业有单独的缓存。

public class QuartzContext
{

    private static readonly string _cacheKey = "STRUCTUREMAP-INSTANCES";

    [ThreadStatic]
    private static JobExecutionContext _jobExecutionContext;

    protected static void Register(JobExecutionContext jobExecutionContext)
    {
        _jobExecutionContext = jobExecutionContext;
        _jobExecutionContext.Put(_cacheKey, new MainObjectCache());
    }

    public static IObjectCache Cache 
    { 
        get 
        {
            return (IObjectCache)_jobExecutionContext.Get(_cacheKey);
        } 
    }
}  

我有一个名为 BaseJobSingleSession 的抽象类,其他作业从该类派生。这个类扩展了 QuartzContext 类。你可以看到我在job被触发的时候注册了JobExecutionContext。

abstract class BaseJobSingleSession : QuartzContext, IStatefulJob
{
    public override void Execute(JobExecutionContext context)
    {
        Register(context);
        IUnitOfWork unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>();

        try
        {
            unitOfWork.Begin();

            // do stuff ....

            unitOfWork.Commit();
        }
        catch (Exception exception)
        {
            unitOfWork.RollBack();

        }
        finally
        {
            unitOfWork.Dispose();
        }
    }
}

最后我定义了 UoW 的生命周期:

For<IUnitOfWork>().LifecycleIs(new HybridHttpQuartzLifecycle()).Use<UnitOfWork>();

(对于生命周期和上下文类,我查看了 StructureMap 源代码以获得这个想法。)

请分享您的想法、cmets 和建议:>

【讨论】:

    【解决方案2】:

    为什么不为石英作业创建一个新会话?工作单元通常是数据库上的事务操作。我无法想象石英作业与 Web 请求/响应在事务上相关。创建新会话并不昂贵。有这种可能吗?

    【讨论】:

    • 是的。我想为每个石英作业创建一个新会话。但由于我使用的是 UoW 的想法,我希望它在我的应用程序中保持一致。所以我不想直接为石英作业创建会话。我想创建一个 UoW 实例。但同时我想使用 StructureMap 来获取 UoW 的实例。这就是为什么我最终在 StructeMap 中为我的 UoW 定义了一个混合生命周期,这样它就会为每个 http 请求或每个石英作业执行返回适当的 UoW。
    • @kaptan:我同意@rcravens。我所做的是获取 ISessionFactory (ObjectFactory.GetInstance()) 的实例并在我的工作触发时打开一个新会话。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-29
    • 2018-06-07
    • 2020-02-26
    • 1970-01-01
    • 2012-03-21
    • 2012-07-06
    • 1970-01-01
    相关资源
    最近更新 更多