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

mac2022-06-30  25

使用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)