语樊偿 发表于 2025-5-29 20:19:35

NHibernate2.1新特性之Tuplizers

NHibernate2.1新特性之Tuplizers

本节内容
       
[*]什么是Tuplizers       
[*]典型实例
               
[*]1.Domain               
[*]2.代理标记proxy marker               
[*]3.DataProxy               
[*]4.实体初始化               
[*]5.重写PocoEntityTuplizer               
[*]6.实体拦截               
[*]7.EntityFactory               
[*]8.映射               
[*]9.测试       
               
[*]结语
什么是Tuplizers

Tuplizers?这个单词在英文字典里没有解释,和元组(tuple)这个单词有点相似,在NHibernate中应该翻译为元组片断,Tuplizers只在映射中提供,所以叫元组片段映射比较合适。
我们平时一般使用Domain Entity,然后使用来映射,对Domain Entity操作。在NHibernate中,对于Domain Entity的Entity Mode为POCO类型,这时对应的tuplizer知道通过其构造方法来创建一个POCO,再通过其属性访问器来访问POCO属性。
Tuplizers,其完整命名空间是NHibernate.Tuple.Tuplizer,它就是根据给定的NHibernate.EntityMode,来复现片断数据。如果给定的片断数据被认为其是一种数据结构,"tuplizer"就是一个知道如何创建这样的数据结构,以及如何给这个数据结构赋值的东西。
在NHibernate中有NHibernate.Tuple.Entity.IEntityTuplizer和NHibernate.Tuple.Component.IComponentTuplizer两个接口,IEntityTuplizer负责管理上面提到的实体的契约,而IComponentTuplizer则是针对组件的。
下面从NHibernate源码中摘取一个典型的例子来说明Tuplizer的用法。
典型实例

我想映射一个接口,对这个接口按照POCO实体模型进行持久化操作。首先想到应该可以New出来这个接口,使用工厂可以产生出来。在初始化这个接口的时候要重写一些NHibernate默认的POCO行为,在对这个接口赋值的时候拦截一些操作,记录下这个接口。获取接口时候同样也需要拦截。
1.Domain

public interface IUser
{
    int Id { get; set; }
    string Name { get; set; }
}我们需要映射这个接口,但是NHibernate只会去映射类,我们怎么去改写代码让NHibernate可以像类那样去映射接口呢?这就是Tuplizers的功能。
2.代理标记proxy marker

由于这里是特殊需要,我对其这个代理做个标记,如果某个实体可以转换为这个代理标记接口就说明是我重写定义的Domain,
/// <summary>
/// 代理标记
/// 对象实例是代理的一个实例
/// </summary>
public interface IProxyMarker
{
    DataProxyHandler DataHandler { get; }
}3.DataProxy

利用Castle的拦截器IInterceptor接口对这个代理数据进行拦截,例如在获取这个代理数据的时候,让NHibernate按照POCO那样去把其数据保存到一个字典中。
/// <summary>
/// 利用Castle的拦截器,代理数据DataProxy
/// </summary>
public sealed class DataProxyHandler:IInterceptor
{
    private readonly Dictionary<string, object> data = new Dictionary<string, object>(50);
    private readonly string entityName;

    public DataProxyHandler(string entityName, object id)
    {
      this.entityName = entityName;
      data["Id"] = id;
    }

    public string EntityName
    {
      get { return entityName; }
    }

    public IDictionary<string, object> Data
    {
      get { return data; }
    }

    public void Intercept(IInvocation invocation)
    {
      invocation.ReturnValue = null;
      string methodName = invocation.Method.Name;

      if ("get_DataHandler".Equals(methodName))
      {
            invocation.ReturnValue = this;
      }
      else if (methodName.StartsWith("set_"))
      {
            string propertyName = methodName.Substring(4);
            data = invocation.Arguments;
      }
      else if (methodName.StartsWith("get_"))
      {
            string propertyName = methodName.Substring(4);
            object value;
            data.TryGetValue(propertyName, out value);
            invocation.ReturnValue = value;
      }
      else if ("ToString".Equals(methodName))
      {
            invocation.ReturnValue = EntityName + "#" + data["Id"];
      }
      else if ("GetHashCode".Equals(methodName))
      {
            invocation.ReturnValue = GetHashCode();
      }
    }
}4.实体初始化

在映射文件中定义映射,NHibernate提供的IInstantiator接口实现负责初始化实体实例,这里就是使用Castle.DynamicProxy.ProxyGenerator的public object CreateInterfaceProxyWithoutTarget(System.Type interfaceToProxy, System.Type[] additionalInterfacesToProxy, params Castle.Core.Interceptor.IInterceptor[] interceptors )方法创建一个接口代理。
/// <summary>
/// 利用NH2.1新特性Tuplizers提供的IInstantiator接口实现负责初始化实体/组件实例
/// </summary>
public class EntityInstantiator : IInstantiator
{
    private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();
    private readonly Type t;

    public EntityInstantiator(Type entityType)
    {
      t = entityType;
    }

    public object Instantiate()
    {
      return Instantiate(null);
    }

    public object Instantiate(object id)
    {
      return
            proxyGenerator.CreateInterfaceProxyWithoutTarget(t, new[] { typeof(IProxyMarker), t }, new DataProxyHandler(t.FullName, id));
    }
    /// <summary>
    /// 判断是否实例化
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public bool IsInstance(object obj)
    {
      try
      {
            return t.IsInstanceOfType(obj);
      }
      catch (Exception e)
      {
            throw new Exception("could not get handle to entity-name as interface : " + e);
      }
    }
}5.重写PocoEntityTuplizer

这才是我们真正自定义的Tuplizer,在映射中使用,重写NHibernate提供的POCO的PocoEntityTuplizer的初始化方法,返回上面实体初始化类完成的创建一个接口代理。
/// <summary>
/// 重写PocoEntityTuplizer
/// </summary>
public class EntityTuplizer : PocoEntityTuplizer
{
    public EntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) : base(entityMetamodel, mappedEntity) { }

    protected override IInstantiator BuildInstantiator(PersistentClass persistentClass)
    {
      return new EntityInstantiator(persistentClass.MappedClass);
    }
}6.实体拦截

NHibernate可以利用NHibernate.IInterceptor实现拦截这个实体:可以去拦截我们创建一个System.Type代理将出现无法预测的值,在这里我仅仅返回上面定义的IProxyMarker标记数据的实体名称,对于其他类型的实体则返回空值。
/// <summary>
/// 利用NHibernate.IInterceptor对这个实体实现拦截
/// </summary>
public class EntityNameInterceptor : EmptyInterceptor
{
    public override string GetEntityName(object entity)
    {
      return ExtractEntityName(entity) ?? base.GetEntityName(entity);
    }

    private static string ExtractEntityName(object entity)
    {
      // Our custom Proxy instances actually bundle their appropriate entity name,
      //so we simply extract it from there if this represents one of our proxies; otherwise, we return null
      var pm = entity as IProxyMarker;
      if (pm != null)
      {
            var myHandler = pm.DataHandler;
            return myHandler.EntityName;
      }
      return null;
    }
}7.EntityFactory

我们创建一个实体工厂,所谓工厂就是New出来实体的一个制造工厂。我们可以var user = entityFactory.NewEntity()这样初始化一个实体。
public class EntityFactory
{
    private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();

    public T NewEntity<T>()
    {
      Type t = typeof(T);
      return
            (T)
            proxyGenerator.CreateInterfaceProxyWithoutTarget(t, new[] { typeof(IProxyMarker), t },
<class name="IUser">
    <tuplizer class="EntityTuplizer" entity-mode="poco"/>
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Name"/>
</class><class name="IUser">
    <tuplizer class="EntityTuplizer" entity-mode="poco"/>
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Name"/>
</class>             new DataProxyHandler(t.FullName, 0));
    }
}上面那些部分相当于一个前奏,为使用tuplizer做准备,我们可以在映射中使用我们自定义的Tuplizer了。
8.映射

这时需要映射这个接口了,使用映射,这个映射有两个属性,分别为class和entity-mode。在这个例子中我在IUser中按照POCO实体模型自定义EntityTuplizer实现来映射。
<class name="IUser">
    <tuplizer class="EntityTuplizer" entity-mode="poco"/>
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Name"/>
</class>9.测试

测试一下我们的结果吧。分别创建、查询、删除操作吧。

public void UserCrud()
{
    object savedId;
    var user = entityFactory.NewEntity<IUser>();
    user.Name = "李永京";

    using (var session = sessions.OpenSession())
    using (var tx = session.BeginTransaction())
    {
      savedId = session.Save(user);
      tx.Commit();
    }

    using (var session = sessions.OpenSession())
    using (var tx = session.BeginTransaction())
    {
      user = session.Get<IUser>(savedId);
      Assert.That(user, Is.Not.Null);
      Assert.That(user.Name, Is.EqualTo("李永京"));
      session.Delete(user);
      tx.Commit();
    }

    using (var session = sessions.OpenSession())
    using (var tx = session.BeginTransaction())
    {
      user = session.Get<IUser>(savedId);
      Assert.That(user, Is.Null);
      tx.Commit();
    }
}测试结果:
第一个Session:创建对象

第二个Session:查询、删除对象

第三个Session:验证对象

结语

由于NHibernate资料很少,所以我从源码中找到这个例子稍微说明下,大家对Tuplizer有什么好的想法可以回复讨论下咯,我想这个功能的扩展就是如果NHibernate Domain与WPF结合,我需要在所有Domain中实现INotifyPropertyChanged接口,就需要重新实现DataProxyHandler。
NHibernate Q&A
       
[*]欢迎加入NHibernate中文社区,一起分享NHibernate!
希望本文对你有所帮助!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: NHibernate2.1新特性之Tuplizers