NHibernate on WCF

I know that there are a few blog posts out there on this subject, so I feel like I'm repeating myself while writing this.  Bear with me...please.  Over the last month I've been chasing an ISession corruption error in our application with little success.  During that time I spent a lot of time looking at our WCF implementation to ensure that it wasn't some piece of that which was causing the problem.  Needless to say, it wasn't, but I have a much greater appreciation for the extensibility of WCF as a result.  Part of my research/debugging/hacking around/crying was looking into the most appropriate way to create, persist and end a nHibernate ISession object during each individual WCF OperationContract call.  Here's what I figured out and created.

The first, and easiest thing that you need to do is set the ServiceContract Attribute to have an InstanceContextMode =  InstanceContextMode.PerCall.  The key here is that I want per call isolation.  Choosing thie InstanceContextMode will force the creation of a new service context for each client request and ultimately create a new InstanceContext object for each call.  What you're doing here is telling WCF that every call to an OperationContext should be treated as a distinctly separate code execution area.  If you were to leave InstanceContextMode to its default (InstanceContextMode.PerSession) the execution area of the code would create an InstanceContext for the duration of the Channel's life. Basically, that would mean that creating a connection to the WCF service would create a new InstanceContext and all OperationContracts would have access to it until the channel was released.  This will allow you to access items in the InstanceContext across calls, which is not how we want to handle our ISessions.

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[NHibernateContext]
public interface ICustomerServices
{
    [OperationContract]
    IEnumerable<CustomerListingDto> RetrieveListingOfAll();
}

So we've created a service that is set to have PerCall InstanceContexts.  This is the first step to creating and storing the ISession.  Putting an ISession into the InstanceContext requires us to create a custom extension using IExtension<T>.  All our extension needs to do is provide a place to store the ISession object for that OperationContract call.  The extensions on the InstanceContext have a lifetime that lasts only for the duration of the call.  In the end the extension looks like this:

public class NHibernateContextExension : IExtension<InstanceContext>
{
    public NHibernateContextExension(ISession session)
    {
        Session = session;
    }

    public ISession Session { get; private set; }

    public void Attach(InstanceContext owner)
    {}

    public void Detach(InstanceContext owner)
    {}
}

Now that we have that extension we need to get it attached to each and every OperationContract call that is handled by our service.  An IInstanceContextInitializer implementing class is used to trigger the addition of the extension to the InstanceContext.  Every time that an InstanceContext is initialized (which will be on every Operation call since we're using InstanceContextMode.PerSession) the NHibernateContextExtension is added to the newly created InstanceContext.  Also note that it is at this point that the NHibernate ISession object is created and assigned to the InstanceContext's extension.  It's here that the nHibernate session per operation call concept is implemented.

public class NHibernateContextInitializer : IInstanceContextInitializer
{
    public void Initialize(InstanceContext instanceContext, Message message)
    {
        instanceContext.Extensions.Add(
            new NHibernateContextExtension(
                NHibernateFactory.OpenSession()
                )
            );
    }
}

You probably noticed in the NHibernateContextInitializer class above that the ISession object is being created from a NHibernateFactory object.  This object is a wrapper for the native NHibernate ISessionFactory object.  We need it implemented in this way so that we have a technique for accessing a session factory that is created outside of the scope of this little framework.  Later in this post you will see how this object is used and initialized with a valid SessionFactory object.

public static class NHibernateFactory
{
    private static ISessionFactory _sessionFactory;

    public static void Initialize()
    {
        Initialize(new Configuration().Configure().BuildSessionFactory());
    }

    public static void Initialize(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public static ISession OpenSession()
    {
        return _sessionFactory.OpenSession();
    }
}

With a way to attach a new ISession to every InstanceContext that is created, we need to trigger this process on each and every call.  To do that we will attribute the WCF service with a custom attribute.  All we need to do is create a class that is an Attribute and implements the IContractBehavior interface.  During ApplyDispatchBehavior we add an NHibernateContextIntializer to start the entire process of creating the new NHibernate ISession for each call.

public class NHibernateContextAttribute : Attribute, IContractBehavior
{
    public ISessionFactory SessionFactory { private get; set; }

    public void Validate(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint)
    {}

    public void ApplyDispatchBehavior(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceContextInitializers.Add(
            new NHibernateContextInitializer());
    }

    public void ApplyClientBehavior(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            ClientRuntime clientRuntime)
    {}

    public void AddBindingParameters(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            BindingParameterCollection bindingParameters)
    {}

Simply creating a new ISession for each call to the WCF service isn't enough.  We need to be able to work with that ISession object in our data access endeavours. To do that I've created a simple class with a static method on it that retrieves the NHibernateContext Extension from the current InstanceContext.  This is possible because there is only one InstanceContext during the life of each service call.  To make this fall in line with other 'Context' object that we are used to working with (like HttpContext) I've created the static method called Current.

public class NHibernateContext
{
    public static NHibernateContextExension Current()
    {
        return OperationContext.Current.
                InstanceContext.Extensions.
                Find<NHibernateContextExension>();
    }
}

All of that explanation is well and good, but the real trick is how the hell do you use this.  That is fairly simple.  You need to do three things. First you need to create and initialize a nHibernate SessionFactory and inject it into the custom wrapper that is provided in this code.  Normally you would want to do this once for the life of the application as shown by placing this code in the Application_Start of the global.asax file.  This code snippet shows two options for initializing the factory. The first line will automatically load a nHibernate SessionFactory using the hibernate.cfg.xml file that is in the same folder as the IglooCoder.Commons.dll file.  The second line allows more flexibility to initialize a nHibernate SessionFactory in the way that you need or want.

protected void Application_Start(object sender, EventArgs e)
{
    NHibernateFactory.Initialize();
    //or//
    NHibernateFactory.Initialize(new Configuration().Configure().BuildSessionFactory());
}

Second you need to attribute any services with the NHibernateContextAttribute) that will need to access data using nHibernate.

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[NHibernateContext]
public interface ICustomerServices
{
    [OperationContract]
    IEnumerable<CustomerListingDto> RetrieveListingOfAll();
}

The final step is to make use of the ISession object that the framework is creating for each call.  As you can see in the code below there is a bit of a trick to using t

public abstract class BaseRepository<T> : IRepository<T>
{
    public T FetchById(int id)
    {
        return NHibernateContext.Current().Session.Get<T>(id);
    }
}

If you want to use this code and not have to write it all out you can grab the source from the project's Subversion trunk (https://igloocoder.net:8445/svn/IglooCommons/trunk/).  Compile the code running b.bat at the command line and you will find the compiled assemblies in the /compile folder.

My next post will talk about how to maintain testability while using this stuff.

Ideas for implementing this came from Dan Rigsby's posts on WCF extension here and here.

posted @ Monday, July 21, 2008 8:16 PM

安全帽检测数据集数据集名称:安全帽检测数据集 图片数量: - 训练集:1,267张 - 验证集:371张 - 测试集:137张 总计:1,775张现场作业场景图片分类类别:- hard_hat(佩戴安全帽):识别正确佩戴安全防护装备的人员- no_hard_hat(未戴安全帽):检测未佩戴安全帽的违规行为- no_head_wear(未戴头部防护):识别无任何头部防护装备的人员- person(人员):通用人员检测,支持背景人员过滤标注格式:YOLO格式边界框标注,包含精确的目标位置和类别标签,适用于实时目标检测任务。数据来源于真实工地监控和作业场景。1. 智能工地安全监控系统: 实时检测作业人员安全帽佩戴情况,自动触发违规预警,降低工地事故风险1. 安全生产合规审计: 自动分析监控录像,生成安全装备佩戴合规报告,替代人工巡检1. 工业物联网(IIoT)集成: 与智能门禁、设备联锁系统结合,确保进入危险区域人员佩戴防护装备1. 计算机视觉教学研究: 提供高质量工业场景目标检测数据集,支持YOLO等主流框架的算法验证• 场景多样性覆盖: 包含室内外作业、高空施工、设备操作等多场景数据,覆盖不同光照条件和视角• 精准行为识别: 严格区分配戴/未配戴安全装备状态,支持复杂背景下的小目标检测• 工业级实用性: 标注经工程安全专家校验,可直接部署到边缘计算设备实现实时分析• 多任务适配性: 除安全帽检测外,支持人员计数、危险区域闯入检测等衍生应用开发• 合规标准对齐: 标注规范符合OSHA等国际安全标准,满足工业安全生产的监管要求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值