nhibernate不用xml配置映射,使用attribute怎么弄

nhibernate不用xml配置映射,使用attribute怎么弄,第1张

由于在项目中使用了NHibernate来作为ORMapping构建数据访问层,那么就必须要配置Object和DataTable的映射。最早的项目中,我们使用了最传统的XML配置文件的方式编写映射关系,但是这样太麻烦,每次修改class和表时都要去修改对应的XML文件,而且还容易出错,一定有疏忽遗漏的地方,还不容易找出错误,所以在第二个项目中,我们使用了FluentNHibernate的Mapping方式代替XML配置。使用FluentNHibernate的最大好处是降低了出错的机会,因为FluentNhibernate的配置是使用C#来编写,可以智能感知,而且还能编译,不像原始的XML配置,写错了都不知道。publicsealedclassConfigMapping:ClassMap{publicConfigMapping(){Table("CONFIG")Id(x=>x.Id,"CONFIG_ID").GeneratedBy.HiLo("1000000000")Map(x=>x.ConfigKey,"CONFIG_KEY")Map(x=>x.ConfigValue,"CONFIG_VALUE")}}但是使用FluentNHibernate的配置方式仍然是需要编写Mapping代码的,也就意味着,如果我更改class或者DataTable的时候,还要对应的更改该Mapping文件。的修改意味着的风险,为了减少这方面的风险,同时为了减少配置的工作量,所以在最新的项目中采用了FluentNHibernate中的Automapping。我们只需要定义好映射的规则,就可以不对每个表和类分别编写映射配置,而是按照规则进行自动的Mapping工作。这样在修改class或者DataTable时,只需要修改类和表即可,不需要再修改配置文件。要做到Automapping,就一定要定义好严格的命名规范,然后按照规范编写Automapping规则,实现自动化的映射。比如我们可以定义如下的规则:类名和字段名采用每个单词首字母大写的方式而数据库表名和列名使用全部大写,单词之间下划线分割的方式。(比如CostCenter类对应表COST_CENTER)类中的主键使用Id命名,表中的主键使用表名+“_ID”的命名方式。(比如CostCenter中有publicvirtuallongId{getset},对应表中的列COST_CENTER_ID)对于一对多的关系,使用父方的类名作为属性名,表中使用父表的主键列名作为对应的外键列的列名。(比如一个班对应多个学生,在Class类中就有publicvirtualIListStudents{getset},而在Student类中就必须使用Class作为属性名:publicvirtualClassClass{getset})对于SubClass,采用将多个子对象都存在同一个表中的方式实现,使用“TYPE”列作为DiscriminatorColumn,使用之类的类名作为子类的唯一标识。对于多对多的关系,把两个类对应的表名进行排序,将小的排前面,然后将两个表名连接起来,中间使用“_”分割。(比如Course和Student是多对多关系,那么产生的中间表表名为COURSE_STUDENT)对于枚举,在数据库中使用tinyint也就是一个Byte来存储,枚举在Automapping中作为UserType进行处理。下面就来编写Automapping的转换规则,首先对String写一个扩展方法,实现CostCenter到COST_CENTER的转换:staticstringToDatabaseName(thisstrings){returnRegex.Replace(s,@"\B[A-Z]",match=>"_"+match.ToString()).ToUpper()}对于1,需要实现IClassConvention实现如下:publicclassClassNameConvention:IClassConvention{publicvirtualvoidApply(IClassInstanceinstance){vartableName=instance.EntityType.Name.ToDatabaseName()instance.Table(tableName)}}同时对于列,需要使用IPropertyConvention接口,实现如下:publicclassPropertyConvention:IPropertyConvention{publicvoidApply(IPropertyInstanceinstance){instance.Column(instance.Name.ToDatabaseName())}}对于2,需要实现IIdConvention接口,另外我们采用的是Hilo值的主键生成方式,使用一个表HIBERNATE_UNIQUE_KEY存储每个表的流水。具体实现如下:publicclassPrimaryKeyConvention:IIdConvention{publicconststringNextHiValueColumnName="VALUE"publicconststringNHibernateHiLoIdentityTableName="HIBERNATE_UNIQUE_KEY"publicconststringTableColumnName="TABLE_NAME"publicvirtualvoidApply(IIdentityInstanceinstance){vartableName=instance.EntityType.Name.ToDatabaseName()instance.Column(tableName+"_ID")//这里设置主键的命名为表名+“_ID”if(instance.Type==typeof(long))//接下来设置主键的生成方式为HiLo值方式{instance.GeneratedBy.HiLo(NHibernateHiLoIdentityTableName,NextHiValueColumnName,"1000000000",builder=>builder.AddParam("where",string.Format("{0}='{1}'",TableColumnName,tableName)))}}}对于3,一对多的情况,需要设置“一”的一方的Collection和“多”的一方的Reference,具体如下:publicclassCollectionConvention:ICollectionConvention{publicvoidApply(ICollectionInstanceinstance){stringcolNamevarentityType=instance.EntityTypevarchildType=instance.ChildTypeif(entityType==childType)//这里是专门对自身关联一对多的情况进行特殊处理,统一使用PARENT_ID作为外键列colName="PARENT_ID"else{colName=entityType.Name.ToDatabaseName()+"_ID"}instance.Key.Column(colName)instance.Cascade.AllDeleteOrphan()}}publicclassReferenceConvention:IReferenceConvention{publicvoidApply(IManyToOneInstanceinstance){stringcolName=nullvarreferenceType=instance.Class.GetUnderlyingSystemType()varentityType=instance.EntityTypevarpropertyName=instance.Property.Name//Selfassociationif(referenceType==entityType)colName="PARENT_ID"elsecolName=propertyName.ToDatabaseName()+"_ID"instance.Column(colName)}}对于4SubClass的处理,需要涉及到指定要进行Discriminate的类,还有DiscriminateColumn,然后指定DiscriminateColumn中如何对Subclass进行Mapping。这里就需要重写DefaultAutomappingConfiguration类,在该类中指定主键、Discriminate的类等,具体代码如下:publicclassAutoMapConfiguration:DefaultAutomappingConfiguration{publicoverrideboolShouldMap(Typetype){return(type.IsClass&&type.Namespace.StartsWith("OurProject.Core.Model"))//指定了哪些类是要进行AutoMapping的}publicoverrideboolIsId(Membermember){returnmember.Name=="Id"//指定了每个类中的Id属性就是该类的主键}publicoverrideboolIsDiscriminated(Typetype)//指定了哪些类是需要进行SubClass继承,将其SubClass都存放在一个表中的。{returntype.In(typeof(Client),typeof(Classification),typeof(MultiClassification))}publicoverridestringGetDiscriminatorColumn(Typetype){return"TYPE"//指定了SubClass的区分列就是有一个叫做TYPE的列}}然后就是关于DiscriminateColumn中的值如何映射成对应的Subclass,需要实现ISubclassConvention接口,代码如下:publicclassSubclassConvention:ISubclassConvention{publicvoidApply(ISubclassInstanceinstance){instance.DiscriminatorValue(instance.EntityType.Name)}}对于5多对多,就需要实现IHasManyToManyConvention接口,在这个接口中对两个表名进行排序,然后进行连接表示中间表。具体代码如下:publicclassHasManyToManyConvention:IHasManyToManyConvention{publicvoidApply(IManyToManyCollectionInstanceinstance){varentityDatabaseName=instance.EntityType.Name.ToDatabaseName()varchildDatabaseName=instance.ChildType.Name.ToDatabaseName()varname=GetTableName(entityDatabaseName,childDatabaseName)//对两个表名进行排序,然后连接组成中间表名instance.Table(name)instance.Key.Column(entityDatabaseName+"_ID")instance.Relationship.Column(childDatabaseName+"_ID")}privatestringGetTableName(stringa,stringb){varr=System.String.CompareOrdinal(a,b)if(r>0){return"{0}_{1}".Fill(b,a)}else{return"{0}_{1}".Fill(a,b)}}}对于6枚举的处理,需要指定枚举为UserType,实现接口IUserTypeConvention,具体代码如下:publicclassEnumConvention:IUserTypeConvention{publicvoidAccept(IAcceptanceCriteriacriteria){criteria.Expect(x=>x.Property.PropertyType.IsEnum)}publicvoidApply(IPropertyInstanceinstance){instance.CustomType(instance.Property.PropertyType)}}实现了以上这几个接口,那么大部分情况下的Automapping都可以实现了。最后是将这些接口通知给FluentNhibernate,让其应用这些接口,导入指定Assembly中的DomainModel,具体的实现方法是:publicvirtualAutoPersistenceModelGenerate(string[]domainAssemblies,string[]dalAssemblies){varmappings=AutoMap.Assemblies(newAutoMapConfiguration(),domainAssemblies.Select(Assembly.LoadFrom).ToArray())foreach(varignoredBaseTypeinIgnoredBaseTypes){mappings.IgnoreBase(ignoredBaseType)}foreach(varincludeBaseTypeinIncludeBaseTypes){mappings.IncludeBase(includeBaseType)}mappings.Conventions.Setup(GetConventions())//指定了Automapping转换的接口实现foreach(vardalAssemblyindalAssemblies){mappings.UseOverridesFromAssembly(Assembly.LoadFrom(dalAssembly))}returnmappings}publicstaticIListIgnoredBaseTypes=newList//这里列出的类都是些Base类,不会Mapping到具体某个表{typeof(Entity)//该对象其实就只有Id这个属性,作为所有要Mapping的类的父类}publicstaticIListIncludeBaseTypes=newList//默认情况下抽象类是不会Mapping成表的,所以这里需要指明这些类是要Mapping成表的{typeof(Classification),typeof(MultiClassification),typeof(Client)}protectedActionGetConventions(){returnfinder=>{finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()finder.Add()}}该方法返回了一个AutoPersistenceModel,使用这个对象注册到NHibernate中即可。

本帖最后由 Bcbjbb 于 2014-1-25 21:01 编辑

看到一个差不多的题目,但看到最后都不知道答案。这个问题折磨我很久了,特意注册进来问问各位大侠,特别是对hilo不大感冒的白板。先介绍一下我的情况,本人小白先后进了拜事通HBA1和lcd3,因为有不少 cd又进了个M1cdt,本来想入DAC2HGC,但撞上Hilo风头正劲所以hold住了。本来还有个lyra2的,但不知道能不能挂转盘,而且不能平衡接入HBA1所以放弃了。大多数评论都说Hilo比DAC2高一头但是纠结的有以下几点:

1.都说Hilo比DAC2高一头,但预算差不多到头了,为了这一头要超预算4,5千吧(包括电源,自有白金星)值不值当。不换的话Lcd3可以换换线,或上个好点的界面什么的预算还不超。

2.人在广州Hilo什么的没得试听不知和我的系统搭不搭。

3.Hilo听说又要电源,时钟什么的拖一大堆,还要设置这设置那,我是小白一没背景二没技术,连这个拉那个设置东设置西的对自己的动手能力十分没信心,若不是Hilo问题是我自己搞出恶声又没人帮忙那就屎了。

4白板好像对Hilo十分不感冒,态度暧昧,常常有的没的冒个头看得我心惊胆战。

最后,先警告各位大侠本人有点精神洁癖是不会收和出二手的,所以基本是一次投资,近期这几大件是不会换的了,把DAC搞定了就要进入养孩子的行列了,要有这次这样的预算要看几年或更久以后了。最后的最后先谢过各位大侠。

由于在项目中使用了NHibernate来作为ORMapping构建数据访问层,那么就必须要配置Object和DataTable的映射。最早

的项目中,我们使用了最传统的XML配置文件的方式编写映射关系,但是这样太麻烦,每次修改class和表时都要去修改对应的XML文件,而且还容易出

错,一定有疏忽遗漏的地方,还不容易找出错误,所以在第二个项目中,我们使用了Fluent

NHibernate的Mapping方式代替XML配置。使用Fluent NHibernate的最大好处是降低了出错的机会,因为Fluent

Nhibernate的配置是使用C#来编写,可以智能感知,而且还能编译,不像原始的XML配置,写错了都不知道。

public sealed class ConfigMapping : ClassMap<Config>

{

public ConfigMapping()

{

Table("CONFIG")

Id(x =>x.Id, "CONFIG_ID").GeneratedBy.HiLo("1000000000")

Map(x =>x.ConfigKey, "CONFIG_KEY")

Map(x =>x.ConfigValue, "CONFIG_VALUE")

}

}

但是使用Fluent

NHibernate的配置方式仍然是需要编写Mapping代码的,也就意味着,如果我更改class或者DataTable的时候,还要对应的更改该

Mapping文件。更多的修改意味着更多的风险,为了减少这方面的风险,同时为了减少配置的工作量,所以在最新的项目中采用了Fluent

NHibernate中的Automapping。我们只需要定义好映射的规则,就可以不对每个表和类分别编写映射配置,而是按照规则进行自动的

Mapping工作。这样在修改class或者DataTable时,只需要修改类和表即可,不需要再修改配置文件。

要做到Automapping,就一定要定义好严格的命名规范,然后按照规范编写Automapping规则,实现自动化的映射。比如我们可以定义如下的规则:

类名和字段名采用每个单词首字母大写的方式而数据库表名和列名使用全部大写,单词之间下划线分割的方式。(比如CostCenter类对应表COST_CENTER)

类中的主键使用Id命名,表中的主键使用表名+“_ID”的命名方式。(比如CostCenter中有public virtual long Id{getset},对应表中的列COST_CENTER_ID)

对于一对多的关系,使用父方的类名作为属性名,表中使用父表的主键列名作为对应的外键列的列名。(比如一个班对应多个学生,在Class类中就有

public virtual IList<Student>

Students{getset},而在Student类中就必须使用Class作为属性名:public virtual Class

Class{getset})

对于SubClass,采用将多个子对象都存在同一个表中的方式实现,使用“TYPE”列作为DiscriminatorColumn,使用之类的类名作为子类的唯一标识。

对于多对多的关系,把两个类对应的表名进行排序,将小的排前面,然后将两个表名连接起来,中间使用“_”分割。(比如Course和Student是多对多关系,那么产生的中间表表名为COURSE_STUDENT)

对于枚举,在数据库中使用tinyint也就是一个Byte来存储,枚举在Automapping中作为UserType进行处理。

下面就来编写Automapping的转换规则,首先对String写一个扩展方法,实现CostCenter到COST_CENTER的转换:

static string ToDatabaseName(this string s)

{

return Regex.Replace(s, @"\B[A-Z]", match =>"_" + match.ToString()).ToUpper()

}

对于1,需要实现IClassConvention实现如下:

public class ClassNameConvention : IClassConvention

{

public virtual void Apply(IClassInstance instance)

{

var tableName = instance.EntityType.Name.ToDatabaseName()

instance.Table(tableName)

}

}

同时对于列,需要使用IPropertyConvention接口,实现如下:

public class PropertyConvention : IPropertyConvention

{

public void Apply(IPropertyInstance instance)

{

instance.Column(instance.Name.ToDatabaseName())

}

}

对于2,需要实现IIdConvention接口,另外我们采用的是Hilo值的主键生成方式,使用一个表HIBERNATE_UNIQUE_KEY存储每个表的流水。具体实现如下:

public class PrimaryKeyConvention : IIdConvention

{

public const string NextHiValueColumnName = "VALUE"

public const string NHibernateHiLoIdentityTableName = "HIBERNATE_UNIQUE_KEY"

public const string TableColumnName = "TABLE_NAME"

public virtual void Apply(IIdentityInstance instance)

{

var tableName = instance.EntityType.Name.ToDatabaseName()

instance.Column(tableName + "_ID")//这里设置主键的命名为表名+“_ID”

if (instance.Type == typeof(long))//接下来设置主键的生成方式为HiLo值方式

{

instance.GeneratedBy.HiLo(

NHibernateHiLoIdentityTableName,

NextHiValueColumnName,

"1000000000",

builder =>builder.AddParam("where", string.Format("{0} = '{1}'", TableColumnName, tableName)))

}

}

}

对于3,一对多的情况,需要设置“一”的一方的Collection和“多”的一方的Reference,具体如下:

public class CollectionConvention : ICollectionConvention

{

public void Apply(ICollectionInstance instance)

{

string colName

var entityType = instance.EntityType

var childType = instance.ChildType

if (entityType == childType)//这里是专门对自身关联一对多的情况进行特殊处理,统一使用PARENT_ID作为外键列

colName = "PARENT_ID"

else

{

colName = entityType.Name.ToDatabaseName() + "_ID"

}

instance.Key.Column(colName)

instance.Cascade.AllDeleteOrphan()

}

}

public class ReferenceConvention : IReferenceConvention

{

public void Apply(IManyToOneInstance instance)

{

string colName = null

var referenceType = instance.Class.GetUnderlyingSystemType()

var entityType = instance.EntityType

var propertyName = instance.Property.Name

//Self association

if (referenceType == entityType)

colName = "PARENT_ID"

else

colName = propertyName.ToDatabaseName() + "_ID"

instance.Column(colName)

}

}

对于4SubClass的处理,需要涉及到指定要进行Discriminate的类,还有DiscriminateColumn,然后指定DiscriminateColumn中如何对Subclass进行Mapping。

这里就需要重写DefaultAutomappingConfiguration类,在该类中指定主键、Discriminate的类等,具体代码如下:

public class AutoMapConfiguration : DefaultAutomappingConfiguration

{

public override bool ShouldMap(Type type)

{

return (type.IsClass &&type.Namespace.StartsWith("OurProject.Core.Model"))//指定了哪些类是要进行AutoMapping的

}

public override bool IsId(Member member)

{

return member.Name == "Id"//指定了每个类中的Id属性就是该类的主键

}

public override bool IsDiscriminated(Type type)//指定了哪些类是需要进行SubClass继承,将其SubClass都存放在一个表中的。

{

return type.In(

typeof(Client),

typeof (Classification),

typeof (MultiClassification)

)

}

public override string GetDiscriminatorColumn(Type type)

{

return "TYPE"//指定了SubClass的区分列就是有一个叫做TYPE的列

}

}

然后就是关于DiscriminateColumn中的值如何映射成对应的Subclass,需要实现ISubclassConvention接口,代码如下:

public class SubclassConvention : ISubclassConvention

{

public void Apply(ISubclassInstance instance)

{

instance.DiscriminatorValue(instance.EntityType.Name)

}

}

对于5多对多,就需要实现IHasManyToManyConvention接口,在这个接口中对两个表名进行排序,然后进行连接表示中间表。具体代码如下:

public class HasManyToManyConvention : IHasManyToManyConvention

{

public void Apply(IManyToManyCollectionInstance instance)

{

var entityDatabaseName = instance.EntityType.Name.ToDatabaseName()

var childDatabaseName = instance.ChildType.Name.ToDatabaseName()

var name = GetTableName(entityDatabaseName, childDatabaseName)//对两个表名进行排序,然后连接组成中间表名

instance.Table(name)

instance.Key.Column(entityDatabaseName + "_ID")

instance.Relationship.Column(childDatabaseName + "_ID")

}

private string GetTableName(string a, string b)

{

var r = System.String.CompareOrdinal(a, b)

if (r >0)

{

return "{0}_{1}".Fill(b, a)

}

else

{

return "{0}_{1}".Fill(a, b)

}

}

}

对于6枚举的处理,需要指定枚举为UserType,实现接口IUserTypeConvention,具体代码如下:

public class EnumConvention : IUserTypeConvention

{

public void Accept(IAcceptanceCriteria<IPropertyInspector>criteria)

{

criteria.Expect(x =>x.Property.PropertyType.IsEnum)

}

public void Apply(IPropertyInstance instance)

{

instance.CustomType(instance.Property.PropertyType)

}

}

实现了以上这几个接口,那么大部分情况下的Automapping都可以实现了。最后是将这些接口通知给FluentNhibernate,让其应用这些接口,导入指定Assembly中的DomainModel,具体的实现方法是:

public virtual AutoPersistenceModel Generate(string[] domainAssemblies, string[] dalAssemblies)

{

var mappings = AutoMap.Assemblies(

new AutoMapConfiguration(), domainAssemblies.Select(Assembly.LoadFrom).ToArray())

foreach (var ignoredBaseType in IgnoredBaseTypes)

{

mappings.IgnoreBase(ignoredBaseType)

}

foreach (var includeBaseType in IncludeBaseTypes)

{

mappings.IncludeBase(includeBaseType)

}

mappings.Conventions.Setup(GetConventions())//指定了Automapping转换的接口实现

foreach (var dalAssembly in dalAssemblies)

{

mappings.UseOverridesFromAssembly(Assembly.LoadFrom(dalAssembly))

}

return mappings

}

public static IList<Type>IgnoredBaseTypes = new List<Type>//这里列出的类都是些Base类,不会Mapping到具体某个表

{

typeof (Entity) //该对象其实就只有Id这个属性,作为所有要Mapping的类的父类

}

public static IList<Type>IncludeBaseTypes = new List<Type>//默认情况下抽象类是不会Mapping成表的,所以这里需要指明这些类是要Mapping成表的

{

typeof (Classification),

typeof (MultiClassification),

typeof(Client)

}

protected Action<IConventionFinder>GetConventions()

{

return finder =>

{

finder.Add<ClassNameConvention>()

finder.Add<PrimaryKeyConvention>()

finder.Add<CollectionConvention>()

finder.Add<ReferenceConvention>()

finder.Add<HasManyConvention>()

finder.Add<SubClassNameConvention>()

finder.Add<SubclassConvention>()

finder.Add<PropertyConvention>()

finder.Add<EnumConvention>()

finder.Add<HasManyToManyConvention>()

}

}

该方法返回了一个AutoPersistenceModel,使用这个对象注册到NHibernate中即可。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/11339442.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存