使用NHibernate 3.2实现Repository(ORuM)(四)NHibernate、Mapping、Mapping-By-Code、AutoMapping...

使用NHibernate 3.2实现Repository(ORuM)(二)、使用NHibernate 3.2实现Repository(ORuM)(三)介绍了使用NHibernate 3.2 Mapping-By-Code实现Repository的方法,Mapping-By-Code相比手工编写xml映射文件HBM的方法更为简便、流畅。
但本实现方法的核心是“ORuM(Object Relational un-Mapping))”,即利用实体、值对象定义实现自动生成HbmMapping,使仓储对象更像个真正意义上的实体对象仓库。只要把实体对象放进仓库,就能拿得出来,无须关注更多细节,只需定义一个仓储对象而已。

IRepository<int, Genre> genreRepository = new NHibernateRepository<int, Genre>(session);
genreRepository.Save(genre);
IList<Genre> genreList = genreRepository.FindAll();

 

NHibernate对Mapping的处理过程,就是将XML文件反序列化为HbmMapping类,再将HbmMapping类Bind为Mappings,最后Bind SessionFactory。本实现方法就是利用实体、值对象定义实现自动生成HbmMapping类,完全避免了编写XML文件和反序列化。


本方法的核心是定义一个EntityMapping泛型类,做为NHibernate.Mapping.ByCode.Conformist.ClassMapping子类

public class EntityMapping<TEntity> : ClassMapping<TEntity> where TEntity : EntityBase
{
public EntityMapping()
{

}
}

EntityMapping类继承了ClassMapping所有的属性和方法。

ClassMapping类的部分方法

public void Property<TProperty>(Expression<Func<TEntity, TProperty>> property, Action<IPropertyMapper> mapping);
public void Id<TProperty>(Expression<Func<TEntity, TProperty>> idProperty, Action<IIdMapper> idMapper);
public void Version<TProperty>(Expression<Func<TEntity, TProperty>> versionProperty, Action<IVersionMapper> versionMapping);
public void ManyToOne<TProperty>(Expression<Func<TEntity, TProperty>> property, Action<IManyToOneMapper> mapping) where TProperty : class;
public void Bag<TElement>(Expression<Func<TEntity, IEnumerable<TElement>>> property, Action<IBagPropertiesMapper<TEntity, TElement>> collectionMapping, Action<ICollectionElementRelation<TElement>> mapping);
public void Component<TComponent>(Expression<Func<TEntity, TComponent>> property, Action<IComponentMapper<TComponent>> mapping) where TComponent : class;

这些方法都是泛型方法,参数为Lambda表达式。

 

使用System.Type GetProperties方法获取实体类属性信息

Type type = typeof(TEntity);               
PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{

}

 

动态生成Lambda表达式参数,动态调用泛型方法

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
this.GetType().GetMethod("Property2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });

 

一般属性 public virtual string Name { get; set; }

//Property(x => x.Name);
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
this.GetType().GetMethod("Property2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });

ID属性 public virtual int Id { get; set; }

if (propName == "Id")
{
//Id(x => x.Id)
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
var o = this.GetType().GetMethod("Id2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });
continue;
}

Version属性 public virtual int Version{ get; set; }

if (propName == "Version")
{
//Version(x => x.Version)
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
var o = this.GetType().GetMethod("Version2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });
continue;
}

ManyToOne属性 public virtual Genre Genre { get; set; }

if (prop.PropertyType.IsSubclassOf(typeof(EntityBase)))
{
//ManyToOne(x => x.Genre);
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
this.GetType().GetMethod("ManyToOne2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });
continue;
}

OneToMany属性 public virtual IEnumerable<Album> Albums { get; set; }

f (prop.PropertyType.IsGenericCollection()
&& prop.PropertyType.DetermineCollectionElementType().IsSubclassOf(typeof(EntityBase)))
{
//Bag(x => x.Genre);
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
this.GetType().GetMethod("Bag2")
.MakeGenericMethod(prop.PropertyType.DetermineCollectionElementType())
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });
continue;
}

Component属性 public virtual Address Address { get; set; }

if (prop.PropertyType.GetInterfaces().Contains<Type>(typeof(IValueObject)))
{
//Component(x => x.Address);
var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);
this.GetType().GetMethod("Component2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });
continue;
}

完整代码如下

EntityMapping.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate.Mapping.ByCode.Conformist;
using NHibernate.Mapping.ByCode;
using System.Reflection;
using System.Linq.Expressions;
using System.Diagnostics;

namespace MVCQuick.Framework.Repository.NHibernate
{
public class EntityMapping<TEntity> : ClassMapping<TEntity> where TEntity : EntityBase
{
public EntityMapping()
{
try
{
Type type = typeof(TEntity);

PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (PropertyInfo prop in props)
{
string propName = prop.Name;

if (propName == "Id")
{
//Id(x => x.Id)

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

var o = this.GetType().GetMethod("Id2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });

continue;
}

if (propName == "Version")
{
//Version(x => x.Version)

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

var o = this.GetType().GetMethod("Version2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });

continue;
}



if (prop.PropertyType.IsSubclassOf(typeof(EntityBase)))
{
//ManyToOne(x => x.Genre);

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

this.GetType().GetMethod("ManyToOne2").MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });

continue;
}

if (prop.PropertyType.IsGenericCollection()
&& prop.PropertyType.DetermineCollectionElementType().IsSubclassOf(typeof(EntityBase)))
{
//Bag(x => x.Genre);

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

this.GetType().GetMethod("Bag2")
.MakeGenericMethod(prop.PropertyType.DetermineCollectionElementType())
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });

continue;
}

if (prop.PropertyType.GetInterfaces().Contains<Type>(typeof(IValueObject)))
{
//Component(x => x.Address);

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

this.GetType().GetMethod("Component2").MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target) });

continue;
}


if (true)
{
//Property(x => x.Name);

var target = Expression.Parameter(type);
var getPropertyValue = Expression.Property(target, prop);

this.GetType().GetMethod("Property2")
.MakeGenericMethod(prop.PropertyType)
.Invoke(this, new object[] { Expression.Lambda(getPropertyValue, target), });
}
}
}
catch (Exception ex)
{
throw ex;
}
}

public void Id2<TProperty>(Expression property)
{
Id((Expression<Func<TEntity, TProperty>>)property,
x => { });
}

public void Version2<TProperty>(Expression property)
{
Version((Expression<Func<TEntity, TProperty>>)property,
x => { });
}

public void Property2<TProperty>(Expression property)
{
Property((Expression<Func<TEntity, TProperty>>)property,
x => { });
}

public void Component2<TComponent>(Expression property) where TComponent : class
{
Component((Expression<Func<TEntity, TComponent>>)property, ComponentMap<TComponent>.Mapping());
}

public void ManyToOne2<TProperty>(Expression property) where TProperty : class
{
ManyToOne((Expression<Func<TEntity, TProperty>>)property,
x => { });
}

public void Bag2<TElement>(Expression property)
{
Bag((Expression<Func<TEntity, IEnumerable<TElement>>>)property,
cm => { cm.Cascade(Cascade.All); cm.Inverse(true); },
x => { });
}
}


internal class ComponentMap<TComponent> where TComponent : class
{
public static void Property<TProperty>(IComponentMapper<TComponent> cm, string propName)
{
var target = Expression.Parameter(typeof(TComponent));

cm.Property<TProperty>((Expression<Func<TComponent, TProperty>>)Expression.Lambda(
Expression.Property(target, propName), target));
}

public static Action<IComponentMapper<TComponent>> Mapping()
{
return cm =>
{
PropertyInfo[] props = typeof(TComponent).GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (var prop in props)
{
typeof(ComponentMap<TComponent>).GetMethod("Property")
.MakeGenericMethod(prop.PropertyType)
.Invoke(null, new object[] { cm, prop .Name});
}
};
}

}
}

扩展ModelMapper

ModelMapperExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate.Mapping.ByCode;
using NHibernate;
using System.Linq.Expressions;
using System.Reflection;

namespace MVCQuick.Framework.Repository.NHibernate
{

public static class ModelMapperExtensions
{
public static void AddEntityMappings(this ModelMapper mapper, string prefix = "", params Assembly[] assemblies)
{
mapper.BeforeMapClass += (mi, t, cam) =>
{
cam.Id(x =>
{
x.Column(t.Name + "ID");
x.Generator(Generators.Identity);
});
cam.Table(prefix+t.Name + "s");
};

foreach (var assembly in assemblies)
{
Type[] types = assembly.GetTypes();

List<Type> entityMappingTypes = new List<Type>();


foreach (var type in types)
{
if (type.IsSubclassOf(typeof(EntityBase)))
{

Type entityMappingType = typeof(EntityMapping<>);
Type genericType = entityMappingType.MakeGenericType(new Type[] { type });

object mappingInstance;

try
{
mappingInstance = Activator.CreateInstance(genericType);
}
catch (Exception e)
{
throw new MappingException("Unable to instantiate mapping class (see InnerException): " + genericType, e);
}

var mapping = mappingInstance as IConformistHoldersProvider;
if (mapping == null)
{
throw new ArgumentOutOfRangeException("type", "The mapping class must be an implementation of IConformistHoldersProvider.");
}

mapper.AddMapping(mapping);

}
}
}
}
}

}

 

将(三)中单元测试代码

ModelMapper mapper = new ModelMapper(new EntityModelInspector());
mapper.BeforeMapClass += (mi, t, cam) =>
{
cam.Id(x =>
{
x.Column(t.Name + "ID");
x.Generator(Generators.Identity);
});
cam.Table(t.Name + "s");
};
mapper.AddMappings(typeof(EntityBase).Assembly.GetExportedTypes());

修改为

ModelMapper mapper = new ModelMapper(new EntityModelInspector());
mapper.AddEntityMappings(null, typeof(EntityBase).Assembly);

 

单元测试代码

RepositoryTests.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using NHibernate.Mapping.ByCode;
using MVCQuick.Framework.Repository.NHibernate;
using MVCQuick.Models;
using MVCQuick.Framework.Repository;
using NHibernate;
using System.Diagnostics;
using System.Reflection;
using MVCQuick.Framework;
using NHibernate.Cfg.MappingSchema;

namespace MVCQuick.Tests
{

[TestFixture]
public class RepositoryTests
{
[Test]
public void SaveEntity()
{
ModelMapper mapper = new ModelMapper(new EntityModelInspector());

mapper.BeforeMapClass += (mi, t, cam) =>
{
cam.Id(x =>
{
x.Column(t.Name + "ID");
x.Generator(Generators.Identity);
});
cam.Table(t.Name + "s");
};

mapper.AddMappings(typeof(EntityBase).Assembly.GetExportedTypes());


var hbmMappings = mapper.CompileMappingForAllExplicitlyAddedEntities();


Debug.WriteLine(hbmMappings.AsString());



using (SQLiteDatabaseProvider provider = new SQLiteDatabaseProvider())
{
provider.AddMappings(hbmMappings, "Repository.Tests");
provider.BuildSchema();
ISession session = provider.OpenSession();

IRepository<int, Genre> genreRepository =
new NHibernateRepository<int, Genre>(session);
Genre genre = new Genre { Name = "Genre-aa", Description = "aaaa" };
genreRepository.Save(genre);
IList<Genre> genreList = genreRepository.FindAll();
Assert.AreEqual(genreList.Count, 1);
Assert.AreEqual(genreList[0].Name, "Genre-aa");
Assert.AreEqual(genreList[0].Description, "aaaa");


IRepository<int, Artist> artistRepository =
new NHibernateRepository<int, Artist>(session);
Artist artist = new Artist { Name = "Artist-bb" };
artistRepository.Save(artist);
IList<Artist> artistList = artistRepository.FindAll();
Assert.AreEqual(artistList.Count, 1);
Assert.AreEqual(artistList[0].Name, "Artist-bb");

Debug.WriteLine("genre Id:" + genre.Id);
Debug.WriteLine("genre HashCode:" + genre.GetHashCode());
Debug.WriteLine("artist Id:" + artist.Id);
Debug.WriteLine("artist HashCode:" + artist.GetHashCode());
Assert.AreNotEqual(genre, artist);

IRepository<int, Album> albumRepository =
new NHibernateRepository<int, Album>(session);
Album album = new Album { Title = "Album-CC", Genre = genre, Artist = artist };
albumRepository.Save(album);
album = new Album { Title = "Album-DD", Genre = genre, Artist = artist };
albumRepository.Save(album);
IList<Album> albumtList = albumRepository.FindAll();
Assert.AreEqual(albumtList.Count, 2);
Assert.AreEqual(albumtList[0].Title, "Album-CC");
Assert.AreEqual(albumtList[1].Title, "Album-DD");
Assert.AreEqual(albumtList[0].Genre.Name, "Genre-aa");
Assert.AreEqual(albumtList[1].Genre.Name, "Genre-aa");

IList<Album> albumtList2 = albumRepository.Find("Title", "Album-DD");
Assert.AreEqual(albumtList2.Count, 1);



Debug.WriteLine("genre Version:" + genre.Version);
Assert.AreEqual(genre.Albums, null);
genre.Albums = new List<Album>();
((List<Album>)genre.Albums).Add(albumtList[0]);
((List<Album>)genre.Albums).Add(albumtList[1]);
genreRepository.Save(genre);
Debug.WriteLine("genre Version:" + genre.Version);
genreList = genreRepository.FindAll();
Assert.AreEqual(genreList[0].Albums.Count<Album>(), 2);

genreList = genreRepository.FindAll();
Assert.AreEqual(genreList.Count, 1);
genreRepository.Delete(genre);
genreList = genreRepository.FindAll();
Assert.AreEqual(genreList.Count, 0);

albumtList = albumRepository.FindAll();
Assert.AreEqual(albumtList.Count, 0);


artistList = artistRepository.FindAll();
Assert.IsNull(artistList[0].Address);
artist.Address = new Address();
artist.Address.City = "Beijing";
artist.Address.Country = "China";
artistRepository.Update(artist);
artistList = artistRepository.FindAll();
Assert.AreEqual(artistList[0].Address.City, "Beijing");
Assert.AreEqual(artistList[0].Address.Country, "China");


}
}
}
}

 

测试结果及测试输出同(三),但(三)中ClassMappings.cs不需要,已实现AutoMapping。

 

源代码下载:http://mvcquick.codeplex.com/

转载于:https://www.cnblogs.com/guyoung/archive/2011/10/07/2200874.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值