迷恋弦哥

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Mapping with ConfORM

ConfORM映射

ConfORM项目使得NHibernate可以使用基于约定的映射.本节将介绍使用ConfORM约定来映射的你的模型(实体类).

准备工作:

1.   从Google代码:http://code.google.com/p/codeconform/source/checkout 签出ConfORM源代码
2.   编译ConfORM项目.
3.   完成前面和Eg.Core相关的模型和映射的章节.

如何去做:

1.   创建一个新的名为Eg.ConfORMMappings控制台项目.
2.   添加对Eg.Core项目以及ConfORM.dll和ConfORM.Shop.dll的引用.
3.   在Eg.Core.Entity中, 更改属性Version的修饰符为public
4.   在Program.cs, 在文件头部添加下列using 语句:

View Code
using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using ConfOrm;
using ConfOrm.NH;
using ConfOrm.Patterns;
using ConfOrm.Shop.CoolNaming;
using Eg.Core;
using NHibernate;
using NHibernate.Cfg.MappingSchema;

5.   在Program类中添加下述的GetMapping函数:

View Code
private static HbmMapping GetMapping()
{
  var orm = new ObjectRelationalMapper();
  var mapper = new Mapper(orm, 
    new CoolPatternsAppliersHolder(orm));
  orm.TablePerClassHierarchy<Product>();
  orm.TablePerClass<ActorRole>();
  orm.Patterns.PoidStrategies.Add(
    new GuidOptimizedPoidPattern());
  orm.VersionProperty<Entity>(x => x.Version);
  orm.NaturalId<Product>(p => p.Name);
  orm.Cascade<Movie, ActorRole>(
    Cascade.All | Cascade.DeleteOrphans);
  mapper.AddPropertyPattern(mi => 
    mi.GetPropertyOrFieldType() == typeof(Decimal) && 
    mi.Name.Contains("Price"), 
    pm => pm.Type(NHibernateUtil.Currency));
  mapper.AddPropertyPattern(mi => 
    orm.IsRootEntity(mi.DeclaringType) && 
    !"Description".Equals(mi.Name), 
    pm => pm.NotNullable(true));
  mapper.Subclass<Movie>(cm => 
    cm.List(movie => movie.Actors, 
    colm => colm.Index(
      lim => lim.Column("ActorIndex")), m => { }));
  var domainClasses = typeof(Entity).Assembly.GetTypes()
    .Where(t => typeof(Entity).IsAssignableFrom(t));
  return mapper.CompileMappingFor(domainClasses);
}

6.   添加下述WriteXmlMapping函数:

View Code
private static void WriteXmlMapping(HbmMapping hbmMapping)
{
  var document = Serialize(hbmMapping);
  File.WriteAllText("WholeDomain.hbm.xml", document);
}

7.   添加下述Serialize函数:

View Code
private static string Serialize(HbmMapping hbmElement)
{
  var setting = new XmlWriterSettings { Indent = true };
  var serializer = new XmlSerializer(typeof(HbmMapping));
  using (var memStream = new MemoryStream(2048))
  using (var xmlWriter = XmlWriter.Create(memStream, setting))
  {
    serializer.Serialize(xmlWriter, hbmElement);
    memStream.Flush();
    memStream.Position = 0;
    using (var sr = new StreamReader(memStream))
    {
      return sr.ReadToEnd();
    }
  }
}

8.   在static void Main 方法中, 添加一行代码:

WriteXmlMapping(GetMapping());

9.   编译运行该程序
10.  查找该程序的bin\Debug文件夹,打开WholeDomain.hbm.xml文件,将会看到下述映射:

View Code
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
namespace="Eg.Core" assembly="Eg.Core" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Product">
    <id name="Id" type="Guid">
      <generator class="guid.comb" />
    </id>
    <discriminator />
    <natural-id>
      <property name="Name" not-null="true" />
    </natural-id>
    <version name="Version" />
    <property name="Description" />
    <property name="UnitPrice" type="Currency" 
      not-null="true" />
  </class>
  <class name="ActorRole">
    <id name="Id" type="Guid">
      <generator class="guid.comb" />
    </id>
    <version name="Version" />
    <property name="Actor" not-null="true" />
    <property name="Role" not-null="true" />
  </class>
  <subclass name="Book" extends="Product">
    <property name="ISBN" />
    <property name="Author" />
  </subclass>
  <subclass name="Movie" extends="Product">
    <property name="Director" />
    <list name="Actors" cascade="all,delete-orphan">
      <key column="MovieId" />
      <list-index column="ActorIndex" />
      <one-to-many class="ActorRole" />
    </list>
  </subclass>
</hibernate-mapping>

分析原理:

  标准的NHibernate应用程序中,NHibernate会获取所有的XML映射文件,并将其串行化到一个HbmMapping对象中,然后将这个HbmMapping对象添加到NHibernate设置中,如下图所示:

  使用ConfORM, 我们就跳过了串行化的步骤,ConfORM映射器可以由我们的约定生成一个HbmMapping对象,该对象将被添加到配置中.
ConfORM使用约定和模式可以从Model直接生成一个映射.除了使用ConfORM的默认模式, 我们还可以自定义约定.
1.   我们先是指定了Product类的层级,她包含Books和Movies类. 然后我们单独添加了ActorRole实体类.
2.   我们使用GuidOptimizedPoidPattern查找到所有名为Id的Guid属性,并使用guid.comb生成器将他们映射为POIDs.
3.   添加从Movie到ActorRole的引用,比如我们的Actors集合,应该用cascade="all-delete-orphan".我们使用下述代码以完成该设置:

orm.Cascade<Movie, ActorRole>(
  Cascade.All | Cascade.DeleteOrphans);

4.   然后,我们配置一些约定.在属性名中所有名为Price的decimal属性都应映射为: type="currency".使用下述代码:

mapper.AddPropertyPattern(mi => 
  mi.GetPropertyOrFieldType() == typeof(Decimal) && 
  mi.Name.Contains("Price"), 
  pm => pm.Type(NHibernateUtil.Currency));

5.   在根类型的实体类的属性中,除了Description属性,她们的映射都使用了not-null="true" .请注意,我们使用的是table-per-class映射方式, 所以我们subclasses 不能包含not-null="true"属性. 代码如下:

View Code
mapper.AddPropertyPattern(mi => 
  orm.IsRootEntity(mi.DeclaringType) && 
  !"Description".Equals(mi.Name), 
  pm => pm.NotNullable(true));

6.   最后,映射Movies中的Actors列表,将该列的索引列名设置为ActorIndex代码如下:

View Code
mapper.Subclass<Movie>(cm => 
  cm.List(movie => movie.Actors, 
  colm => colm.Index(
    lim => lim.Column("ActorIndex")), m => { }));

7.   生成HbmMapping对象的最后一步是通过每一个实体的类型调用CompileMappingFor.代码如下:

View Code
var domainClasses = typeof(Entity).Assembly.GetTypes()
  .Where(t => typeof(Entity).IsAssignableFrom(t));
return mapper.CompileMappingFor(domainClasses);

8.   所生成的的mapping对象和XML映射文件:WholeDomain.hbm.xml所包含的内容是等价的.

posted on 2012-06-28 14:52  迷恋弦哥  阅读(361)  评论(0)    收藏  举报