Using dictionaries as entities
使用字典作为实体
NHibernate中一个鲜为人知的特性是EntityMode.Map. 本节介绍如何使用该特性来持久化实体,而不是使用类.
准备
为NHibernate创建一个控制台应用程序,步骤如下:
1. 创建一个控制台应用程序.
2. 添加引用NHibernate.dll,NHibernate.ByteCode.Castle.dll.
3. 在Main函数中添加下面的代码:
var nhConfig = new Configuration().Configure(); var sessionFactory = nhConfig.BuildSessionFactory();
4. 在项目中添加App.config文件.
5. 在configuration 元素中,声明hibernate-configuration节点,代码如下:
<configSections> <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/> </configSections>
6. 在configuration 元素中,添加名为db的连接字符串,代码如下:
<connectionStrings> <add name="db" connectionString="Server=.\SQLExpress; Database=NHCookbook; Trusted_Connection=SSPI"/> </connectionStrings>
7. 添加hibernate-configuration节点节点,代码如下:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> <session-factory> <property name="proxyfactory.factory_class"> NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle </property> <property name="dialect"> NHibernate.Dialect.MsSql2008Dialect, NHibernate </property> <property name="connection.connection_string_name"> db </property> <property name="adonet.batch_size"> 100 </property> <mapping assembly="yourAssemblyHere"/> </session-factory> </hibernate-configuration>
8. 将映射程序集的值改为项目的名字.
步骤
1. 在hibernate-configurationn节点的session-factory元素中,添加值为dynamic-map的default_entity_mode属性.
2. 新建一个映射文件Product.hbm.xml,代码如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class entity-name="Product" discriminator-value="Eg.Core.Product"> <id name="Id" type="Guid"> <generator class="guid.comb" /> </id> <discriminator column="ProductType" type="String" /> <natural-id mutable="true"> <property name="Name" not-null="true" type="String" /> </natural-id> <version name="Version" type="Int32"/> <property name="Description" type="String" /> <property name="UnitPrice" not-null="true" type="Currency" /> </class> </hibernate-mapping>
3. 再创建一个映射文件Movie.hbm.xml,代码如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <subclass entity-name="Movie" extends="Product" discriminator-value="Eg.Core.Movie"> <property name="Director" type="String" /> <list name="Actors" cascade="all-delete-orphan"> <key column="MovieId" /> <index column="ActorIndex" /> <one-to-many entity-name="ActorRole"/> </list> </subclass> </hibernate-mapping>
4. 最后,再创建一个映射文件ActorRole.hbm.xml,代码如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class entity-name="ActorRole"> <id name="Id" type="Guid"> <generator class="guid.comb" /> </id> <version name="Version" type="Int32" /> <property name="Actor" type="String" not-null="true" /> <property name="Role" type="String" not-null="true" /> </class> </hibernate-mapping>
5. 将这些映射文件设置为嵌入式资源.
6. 在Main函数中,构建session factory之后,创建一个movie对象作为字典,代码如下:
var movieActors = new List<Dictionary<string, object>>() { new Dictionary<string, object>() { {"Actor","Keanu Reeves"}, {"Role","Neo"} }, new Dictionary<string, object>() { {"Actor", "Carrie-Ann Moss"}, {"Role", "Trinity"} } }; var movie = new Dictionary<string, object>() { {"Name", "The Matrix"}, {"Description", "Sci-Fi Action film"}, {"UnitPrice", 18.99M}, {"Director", "Wachowski Brothers"}, {"Actors", movieActors} };
7. 创建movie字典后,将其保存,代码如下:
using (var session = sessionFactory.OpenSession()) { using (var tx = session.BeginTransaction()) { session.Save("Movie", movie); tx.Commit(); } }
8. 编译运行.
9. 查看数据库的Product表,ActorRole表.
原理
EntityMode.Map映射允许将实体定义为词典而不再是静态类型. 这种方法分为三个部分.
首先,First, 不再使用默认的EntityMode.Poco来创建会话,使用默认的EntityMode.Poco时,使用类对象和NHibernate进行交互.现在我们使用EntityMode.Map,通过设置default_entity_mode属性的值为dynamic-map来实现.在第一章中,我们了解到,NHibernate来源于Java的Hibernate,NHibernate使用术语map来代替dictionary.
其次,我们对映射做一些微小的改变.首先,你会注意到,我们设置一个entity-name而不是一个class name.这使得我们可以通过名称来指定一个实体,而不在让NHibernate基于我们传入的对象类型来决定. 然后,你会注意到,我们为所有的属性指定了类型,因为我们不再有类,NHibernate也再能通过类来推测我们的数据类型,所以我们必须告诉她.最后,我们指定了discriminator值.记得在第二章中,默认的discriminator值是类型的全名.默认的discriminator值实际上是entity-name(她默认的就是类型的全名).本节中,没有了类型,如果仍旧使用entity-names会造成数据和映射无法匹配.我们覆盖了值,只是为了数据可以完美匹配来自其他示例的数据.
最后,我们使用dictionaries (maps)和entity-name字符串来关联会话,而不在使用带类型的对象.
扩展
虽然这个例子可能看起来有点学术,但随着Dynamic Language Runtime的发布和C#4中新的动态特性,在连接NHibernate和动态语言上,这种类型的应用无疑是很有用的.
Partially dynamic(部分动态)
在整个应用程序中很少需要使用EntityMode.Map,如本节示例所示.但是,在一些特殊的情况下,你或许需要使用她,比如在你不想创建类时.在这样的情况下,我们不在设置default_entity_mode属性,而会在map模式中打开一个子会话.实现的代码,如下所示:
using (var pocoSession = sessionFactory.OpenSession()) { using (var childSession = pocoSession.GetSession(EntityMode.Map)) { // Do something here } }