NHibernate2.1新特性之Tuplizers
创始人
2024-04-24 13:01:39
0

Tuplizers?这个单词在英文字典里没有解释,和元组(tuple)这个单词有点相似,在NHibernate中应该翻译为元组片断,Tuplizers只在映射中提供,所以叫元组片段映射比较合适。

我们平时一般使用Domain Entity,然后使用< class>来映射,对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

  1. public interface IUser  
  2. {  
  3.     int Id { get; set; }  
  4.     string Name { get; set; }  
  5. }  

我们需要映射这个接口,但是NHibernate只会去映射类,我们怎么去改写代码让NHibernate可以像类那样去映射接口呢?这就是Tuplizers的功能。

2.代理标记proxy marker

由于这里是特殊需要,我对其这个代理做个标记,如果某个实体可以转换为这个代理标记接口就说明是我重写定义的Domain,

  1. /// < summary>  
  2. /// 代理标记  
  3. /// 对象实例是代理的一个实例  
  4. /// < /summary>  
  5. public interface IProxyMarker  
  6. {  
  7.     DataProxyHandler DataHandler { get; }  


3.DataProxy

利用Castle的拦截器IInterceptor接口对这个代理数据进行拦截,例如在获取这个代理数据的时候,让NHibernate按照POCO那样去把其数据保存到一个字典中。

  1. /// < summary>  
  2. /// 利用Castle的拦截器,代理数据DataProxy  
  3. /// < /summary>  
  4. public sealed class DataProxyHandler:IInterceptor  
  5. {  
  6.     private readonly Dictionary< stringobject> data = new Dictionary< stringobject>(50);  
  7.     private readonly string entityName;  
  8.  
  9.     public DataProxyHandler(string entityName, object id)  
  10.     {  
  11.         this.entityName = entityName;  
  12.         data["Id"] = id;  
  13.     }  
  14.  
  15.     public string EntityName  
  16.     {  
  17.         get { return entityName; }  
  18.     }  
  19.  
  20.     public IDictionary< stringobject> Data  
  21.     {  
  22.         get { return data; }  
  23.     }  
  24.  
  25.     public void Intercept(IInvocation invocation)  
  26.     {  
  27.         invocation.ReturnValue = null;  
  28.         string methodName = invocation.Method.Name;  
  29.  
  30.         if ("get_DataHandler".Equals(methodName))  
  31.         {  
  32.             invocation.ReturnValue = this;  
  33.         }  
  34.         else if (methodName.StartsWith("set_"))  
  35.         {  
  36.             string propertyName = methodName.Substring(4);  
  37.             data[propertyName] = invocation.Arguments[0];  
  38.         }  
  39.         else if (methodName.StartsWith("get_"))  
  40.         {  
  41.             string propertyName = methodName.Substring(4);  
  42.             object value;  
  43.             data.TryGetValue(propertyName, out value);  
  44.             invocation.ReturnValue = value;  
  45.         }  
  46.         else if ("ToString".Equals(methodName))  
  47.         {  
  48.             invocation.ReturnValue = EntityName + "#" + data["Id"];  
  49.         }  
  50.         else if ("GetHashCode".Equals(methodName))  
  51.         {  
  52.             invocation.ReturnValue = GetHashCode();  
  53.         }   
  54.     }  
  55. }  
  56.  

4.实体初始化

在映射文件中定义< tuplizers>映射,NHibernate提供的IInstantiator接口实现负责初始化实体实例,这里就是使用Castle.DynamicProxy.ProxyGenerator的public object CreateInterfaceProxyWithoutTarget(System.Type interfaceToProxy, System.Type[] additionalInterfacesToProxy, params Castle.Core.Interceptor.IInterceptor[] interceptors )方法创建一个接口代理。

  1. /// < summary> 
  2. /// 利用NH2.1新特性Tuplizers提供的IInstantiator接口实现负责初始化实体/组件实例  
  3. /// < /summary> 
  4. public class EntityInstantiator : IInstantiator  
  5. {  
  6.     private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();  
  7.     private readonly Type t;  
  8.  
  9.     public EntityInstantiator(Type entityType)  
  10.     {  
  11.         t = entityType;  
  12.     }  
  13.  
  14.     public object Instantiate()  
  15.     {  
  16.         return Instantiate(null);  
  17.     }  
  18.  
  19.     public object Instantiate(object id)  
  20.     {  
  21.         return  
  22.             proxyGenerator.CreateInterfaceProxyWithoutTarget(t, new[] { typeof(IProxyMarker), t }, new DataProxyHandler(t.FullName, id));  
  23.     }  
  24.     /// < summary> 
  25.     /// 判断是否实例化  
  26.     /// < /summary> 
  27.     /// < param name="obj">< /param> 
  28.     /// < returns>< /returns> 
  29.     public bool IsInstance(object obj)  
  30.     {  
  31.         try  
  32.         {  
  33.             return t.IsInstanceOfType(obj);  
  34.         }  
  35.         catch (Exception e)  
  36.         {  
  37.             throw new Exception("could not get handle to entity-name as interface : " + e);  
  38.         }  
  39.     }  
  40. }  
  41.  

5.重写PocoEntityTuplizer

这才是我们真正自定义的Tuplizer,在映射中使用,重写NHibernate提供的POCO的PocoEntityTuplizer的初始化方法,返回上面实体初始化类完成的创建一个接口代理。

  1. /// < summary>  
  2. /// 重写PocoEntityTuplizer  
  3. /// < /summary>  
  4. public class EntityTuplizer : PocoEntityTuplizer  
  5. {  
  6.     public EntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) : base(entityMetamodel, mappedEntity) { }  
  7.  
  8.     protected override IInstantiator BuildInstantiator(PersistentClass persistentClass)  
  9.     {  
  10.         return new EntityInstantiator(persistentClass.MappedClass);  
  11.     }  
  12. }  
  13.  

6.实体拦截

NHibernate可以利用NHibernate.IInterceptor实现拦截这个实体:可以去拦截我们创建一个System.Type代理将出现无法预测的值,在这里我仅仅返回上面定义的IProxyMarker标记数据的实体名称,对于其他类型的实体则返回空值。

  1. /// < summary>  
  2. /// 利用NHibernate.IInterceptor对这个实体实现拦截  
  3. /// < /summary>  
  4. public class EntityNameInterceptor : EmptyInterceptor  
  5. {  
  6.     public override string GetEntityName(object entity)  
  7.     {  
  8.         return ExtractEntityName(entity) ?? base.GetEntityName(entity);  
  9.     }  
  10.  
  11.     private static string ExtractEntityName(object entity)  
  12.     {  
  13.         // Our custom Proxy instances actually bundle their appropriate entity name,   
  14.         //so we simply extract it from there if this represents one of our proxies; otherwise, we return null   
  15.         var pm = entity as IProxyMarker;  
  16.         if (pm != null)  
  17.         {  
  18.             var myHandler = pm.DataHandler;  
  19.             return myHandler.EntityName;  
  20.         }  
  21.         return null;  
  22.     }   
  23. }  
  24.  

7.EntityFactory

我们创建一个实体工厂,所谓工厂就是New出来实体的一个制造工厂。我们可以var user = entityFactory.NewEntity< IUser>()这样初始化一个实体。

  1. public class EntityFactory  
  2. {  
  3.     private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();  
  4.  
  5.     public T NewEntity< T>()  
  6.     {  
  7.         Type t = typeof(T);  
  8.         return 
  9.             (T)  
  10.             proxyGenerator.CreateInterfaceProxyWithoutTarget(t, new[] { typeof(IProxyMarker), t },  
  11.                                                              new DataProxyHandler(t.FullName, 0));  
  12.     }  
  13. }  
  14.  

上面那些部分相当于一个前奏,为使用tuplizer做准备,我们可以在映射中使用我们自定义的Tuplizer了。

8.映射

这时需要映射这个接口了,使用< tuplizer>映射,这个映射有两个属性,分别为class和entity-mode。在这个例子中我在IUser中按照POCO实体模型自定义EntityTuplizer实现来映射。

  1. < class name="IUser"> 
  2.     < tuplizer class="EntityTuplizer" entity-mode="poco"/> 
  3.     < id name="Id"> 
  4.         < generator class="hilo"/> 
  5.     < /id> 
  6.     < property name="Name"/> 
  7. < /class> 

9.测试

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

[Test]

  1. public void UserCrud()  
  2. {  
  3.     object savedId;  
  4.     var user = entityFactory.NewEntity< IUser>();  
  5.     user.Name = "李永京";  
  6.  
  7.     using (var session = sessions.OpenSession())  
  8.     using (var tx = session.BeginTransaction())  
  9.     {  
  10.         savedId = session.Save(user);  
  11.         tx.Commit();  
  12.     }  
  13.  
  14.     using (var session = sessions.OpenSession())  
  15.     using (var tx = session.BeginTransaction())  
  16.     {  
  17.         user = session.Get< IUser>(savedId);  
  18.         Assert.That(user, Is.Not.Null);  
  19.         Assert.That(user.Name, Is.EqualTo("李永京"));  
  20.         session.Delete(user);  
  21.         tx.Commit();  
  22.     }  
  23.  
  24.     using (var session = sessions.OpenSession())  
  25.     using (var tx = session.BeginTransaction())  
  26.     {  
  27.         user = session.Get< IUser>(savedId);  
  28.         Assert.That(user, Is.Null);  
  29.         tx.Commit();  
  30.     }  
  31. }  
  32.    

结语

由于NHibernate资料很少,所以我从源码中找到这个例子稍微说明下,大家对Tuplizer有什么好的想法可以回复讨论下咯,我想这个功能的扩展就是如果NHibernate Domain与WPF结合,我需要在所有Domain中实现INotifyPropertyChanged接口,就需要重新实现DataProxyHandler。

【编辑推荐】

  1. 使用LINQ查询泛型字典Dictionary
  2. 浅析Linq to SQL更新数据时容易忽略的问题
  3. 浅谈LINQ to SQL集成数据库语言优劣
  4. LINQ横向对比foreach方法
  5. 浅谈LINQ如何插入删除和更新数据库记录备注

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...