如何使用 FluentNHibernate N:N 映射与附加属性的关系
Posted
技术标签:
【中文标题】如何使用 FluentNHibernate N:N 映射与附加属性的关系【英文标题】:How to map with FluentNHibernate N:N Relationship with additional attribute 【发布时间】:2017-12-25 05:34:33 【问题描述】:我有这个数据库模型:
Candidato 1N Telefone N1 TipoTelefone
我的表格在哪里有这些列
候选人
Id(int - 身份) 命名完成 DataNascimento电话
Id(int - 身份) IdCandidato IdTipoTelefone 数字TipoTelefone
Id(int - 身份) 名称我的类属性是:
Candidato
=========
public virtual int Id get; set;
public virtual string NomeCompleto get; set;
public virtual DateTime DataNascimento get; set;
public virtual IList<Telefone> Telefones get; set;
Telefone
========
public virtual int Id get; set;
public virtual Candidato Candidato get; set;
public virtual TipoTelefone TipoTelefone get; set;
public virtual string Numero get; set;
TipoTelefone
============
public virtual int Id get; set;
public virtual string Descricao get; set;
public IList<Telefone> Telefones get; set;
我正在尝试保存一个 Candidato 及其 Telefones,因为我有一个 Candidato for Many Telefones。
但是当我尝试同时保存我的 Candidato 及其电话时,我遇到了错误。以下是我每次尝试某种尝试时都会遇到的一些错误:
对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例,或将属性的级联操作设置为使其自动保存的内容。类型:Entity.Telefone,实体:Entity.Telefone
对象是一个未保存的瞬态实例 - 合并前保存瞬态实例:Entity.Telefone
该类没有标识符属性:Entity.Telefone
当 IDENTITY_INSERT 设置为 OFF 时,无法在表“Telefone”中插入标识列的显式值。
Não é possível converter um objeto do tipo 'NHibernate.Collection.Generic.PersistentGenericSet1[Entity.Telefone]' no tipo 'System.Collections.Generic.IList
1[Entity.Telefone]'。
对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例,或将属性的级联操作设置为使其自动保存的内容。类型:Entity.Telefone,实体:Entity.Telefone
无法插入:[Entity.Telefone][SQL: INSERT INTO Telefone (Numero, IdTipoTelefone, Id) VALUES (?, ?, ?);选择 SCOPE_IDENTITY()]
我的项目正在使用 NHibernate.Mapping.ByCode,但我怀疑我没有得到正确的地图解决方案。所以,我想知道,如何为这三个实体进行正确的映射。
重要的是要看到我的 N:N Telefone 表有一个属性 Numero,所以这个实体/表不是一个简单的 N:N 实体,而是一个真实的实体,因为它里面有一个自己的属性。
【问题讨论】:
【参考方案1】:这是我的一个老例子。
这是我的“中间”表。
这是一个流利的地图
public class EmployeeToJobTitleMatchLinkMap : ClassMap<EmployeeToJobTitleMatchLinkNHEntity>
public EmployeeToJobTitleMatchLinkMap()
Schema("dbo");
Table("EmployeeToJobTitleMatchLink");
Id(x => x.LinkSurrogateUUID).GeneratedBy.GuidComb();
Map(x => x.PriorityRank);
Map(x => x.JobStartedOnDate);
References(x => x.TheEmployee).Column("TheEmployeeUUID").Not.Nullable().Index("IX_ETJTMLM_TheEmployeeUUID_And_TheJobTitleUUID"); ;/*Bad naming convention with "The", but left here so it can be seen easily in the DDL*/
References(x => x.TheJobTitle).Column("TheJobTitleUUID").Not.Nullable().Index("IX_ETJTMLM_TheEmployeeUUID_And_TheJobTitleUUID"); ;/*Bad naming convention with "The", but left here so it can be seen easily in the DDL*/
这里是(我认为和上面一样)一个hbm.xml文件。
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" schema="dbo" name="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup.Domain.EmployeeToJobTitleMatchLinkNHEntity, MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="EmployeeToJobTitleMatchLink">
<id name="LinkSurrogateUUID" type="System.Nullable`1[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="LinkSurrogateUUID" />
<generator class="guid.comb" />
</id>
<property name="PriorityRank" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="PriorityRank" />
</property>
<property name="JobStartedOnDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="JobStartedOnDate" />
</property>
<many-to-one class="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup.Domain.EmployeeNHEntity, MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="TheEmployee">
<column name="TheEmployeeUUID" index="IX_ETJTMLM_TheEmployeeUUID_And_TheJobTitleUUID" not-null="true" />
</many-to-one>
<many-to-one class="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup.Domain.JobTitleNHEntity, MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Data.NHibernateSetup, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="TheJobTitle">
<column name="TheJobTitleUUID" index="IX_ETJTMLM_TheEmployeeUUID_And_TheJobTitleUUID" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
PriorityRank 和 JobStartedOnDate 将是我的“关系的额外属性”。
这是我找到的另一个 hbm.xml 文件。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue"
namespace="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue.Domain">
<class name="EmployeeToJobTitleMatchLink" lazy="false" schema="MySchema">
<!-- Naming this "Id" might be a requirement, be careful -->
<!--
ISSUE If the generator class is changed from "assigned" to
guid
guid.comb
uuid.string
It will break the ability to create the relationship
-->
<id name="LinkSurrogateUUID">
<generator class="guid.comb"></generator>
</id>
<!--
<id name="LinkSurrogateKey">
<!- -
<generator class="hilo">
<param name="table">HiloHolder</param>
<param name="column">HiloColumn</param>
</generator>
- ->
<generator class="identity">
</generator>
</id>
-->
<many-to-one name="TheEmployee" column="EmployeeUUID" lazy="false" cascade="none" not-null="true" /> <!-- class="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue.Domain.Employee, MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue" -->
<many-to-one name="TheJobTitle" column="JobTitleUUID" lazy="false" cascade="none" not-null="true" /> <!-- class="MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue.Domain.JobTitle, MyCompany.MyTechnology.Prototypes.CompositeAppPOC.Glue" -->
<!--
<many-to-one name="TheEmployee" column="EmployeeKey" lazy="false" cascade="none" not-null="true" />
<many-to-one name="TheJobTitle" column="JobTitleKey" lazy="false" cascade="none" not-null="true" />
-->
<!-- These are "scalar properties of the ~relationship~"-->
<property name="PriorityRank" not-null="true" update="false" />
<property name="JobStartedOnDate" not-null="true" update="false" />
</class>
</hibernate-mapping>
这是我的“中间”域对象
[Serializable]
public partial class EmployeeToJobTitleMatchLink
public EmployeeToJobTitleMatchLink()
//this.Id = Guid.NewGuid(); /* this works in conjuction with <generator class="assigned"></generator> */
//EF Tweaks
public Guid TheEmployeeUUID get; set;
public Guid TheJobTitleUUID get; set;
public Guid? LinkSurrogateUUID get; set;
/* These are "scalar properties of the ~~relationship~~ */
public int PriorityRank get; set;
public DateTime JobStartedOnDate get; set;
public Employee TheEmployee get; set;
public JobTitle TheJobTitle get; set;
也许这会有所帮助。
我曾一度解决了这个问题,但现在已经好几年了。
关键是你有一个域对象,它有额外的标量,每个“边”都有一个对象。
Telefone
========
public virtual int Id get; set;
public virtual Candidato Candidato get; set;
public virtual TipoTelefone TipoTelefone get; set;
public virtual string Numero get; set;
我相信你的 Telefone 课程是正确的。所以我认为问题是你的映射。 Fluent 或 hbm.xml。也许我的“笔记”可以提供帮助。我不能把它放在 cmets 中。
我不得不将“中间”对象添加到“外部”对象中。
例如:
[Serializable]
public partial class Employee
public Employee()
CommonConstructor();
private void CommonConstructor()
this.MyEmployeeToJobTitleMatchLinks = new EmployeeToJobTitleMatchLinkCollection();
//EF Tweaks
public Guid ParentDepartmentUUID get; set;
public Guid? EmployeeUUID get; set;
public byte[] TheVersionProperty get; set;
//public int? EmployeeKey get; set;
// public IDepartment ParentDepartment get; set;
public string SSN get; set;
public string LastName get; set;
public string FirstName get; set;
public DateTime CreateDate get; set;
public DateTime HireDate get; set;
//
public EmployeeToJobTitleMatchLinkCollection MyEmployeeToJobTitleMatchLinks get; set;
public void AddJobTitleLink(EmployeeToJobTitleMatchLink link)
link.TheEmployee = this;
if (!this.MyEmployeeToJobTitleMatchLinks.Contains(link))
this.MyEmployeeToJobTitleMatchLinks.Add(link);
if (!link.TheJobTitle.MyJobTitleToEmployeeMatchLinks.Contains(link))
link.TheJobTitle.MyJobTitleToEmployeeMatchLinks.Add(link);
public void RemoveJobTitleLink(EmployeeToJobTitleMatchLink link)
link.TheEmployee = this;
if (this.MyEmployeeToJobTitleMatchLinks.Contains(link))
this.MyEmployeeToJobTitleMatchLinks.Remove(link);
if (link.TheJobTitle.MyJobTitleToEmployeeMatchLinks.Contains(link))
link.TheJobTitle.MyJobTitleToEmployeeMatchLinks.Remove(link);
//public ICollection<IParkingAreaDeprecated> MyParkingAreas get; set;
//public ICollection<ParkingArea> MyParkingAreas get; set;
public IParkingAreaCollection MyParkingAreas get; set;
//public void AddParkingArea(IParkingAreaDeprecated pa)
public void AddParkingArea(ParkingArea pa)
//pa.MyEmployees.Add(this);
if (!pa.MyEmployees.Contains(this))
//pa.AddEmployee(this);//Causes overflow situation
pa.MyEmployees.Add(this);
if (!this.MyParkingAreas.Contains(pa))
this.MyParkingAreas.Add(pa);
//public void RemoveParkingArea(IParkingAreaDeprecated pa)
public void RemoveParkingArea(ParkingArea pa)
if (pa.MyEmployees.Contains(this))
pa.MyEmployees.Remove(this);
if (this.MyParkingAreas.Contains(pa))
this.MyParkingAreas.Remove(pa);
public override string ToString()
return string.Format("0:1,2", this.SSN , this.LastName , this.FirstName );
和另一个“外部”对象
[Serializable]
public partial class JobTitle : IJobTitle
public JobTitle()
CommonConstructor();
private void CommonConstructor()
//this.MyEmployees = new List<Employee>();
//this.MyJobTitleToEmployeeMatchLinks = new List<EmployeeToJobTitleMatchLink>();
this.MyJobTitleToEmployeeMatchLinks = new EmployeeToJobTitleMatchLinkCollection();
public Guid? JobTitleUUID get; set;
//public int? JobTitleKey get; set;
//public byte[] TheVersionProperty get; set;
public string JobTitleName get; set;
public DateTime CreateDate get; set;
//public ICollection<Employee> MyEmployees get; set;
//public ICollection<IEmployeeToJobTitleMatchLink> MyJobTitleToEmployeeMatchLinks get; set;
//public ICollection<EmployeeToJobTitleMatchLink> MyJobTitleToEmployeeMatchLinks get; set;
public IEmployeeToJobTitleMatchLinkCollection MyJobTitleToEmployeeMatchLinks get; set;
public void AddEmployeeLink(EmployeeToJobTitleMatchLink link)
link.TheJobTitle = this;
if (!this.MyJobTitleToEmployeeMatchLinks.Contains(link))
this.MyJobTitleToEmployeeMatchLinks.Add(link);
if (!link.TheEmployee.MyEmployeeToJobTitleMatchLinks.Contains(link))
link.TheEmployee.MyEmployeeToJobTitleMatchLinks.Add(link);
public void RemoveEmployeeLink(EmployeeToJobTitleMatchLink link)
link.TheJobTitle = this;
if (this.MyJobTitleToEmployeeMatchLinks.Contains(link))
this.MyJobTitleToEmployeeMatchLinks.Remove(link);
if (link.TheEmployee.MyEmployeeToJobTitleMatchLinks.Contains(link))
link.TheEmployee.MyEmployeeToJobTitleMatchLinks.Remove(link);
这是我连接所有内容的代码。
请注意,我有一个以前没有讨论过的部门实体。 重要的部分是 Employee、JobTitle 和“链接”对象
EmployeeController empRepo = null;
JobTitleController jtRepo = null;
Department idept = new Department() DepartmentName = "Department One", CreateDate = DateTime.Now ;
DepartmentController deptRepo = new DepartmentController();
deptRepo.Add(idept);
Employee emp = new Employee() ParentDepartment = idept, SSN = "111111111", CreateDate = DateTime.Now, HireDate = DateTime.Now, LastName = "Smith", FirstName = "John" ;
JobTitle jtDev = new JobTitle() JobTitleName = "Developer", CreateDate = DateTime.Now ;
JobTitle jtProjectManager = new JobTitle() JobTitleName = "Project Manager", CreateDate = DateTime.Now ;
JobTitle jtDba = new JobTitle() JobTitleName = "DBA", CreateDate = DateTime.Now ;
jtRepo = new JobTitleController();
jtRepo.Add(jtDev);
jtRepo.Add(jtProjectManager);
empRepo = new EmployeeController();
empRepo.AddEmployee(emp);
EmployeeToJobTitleMatchLink link1ToAddThruJobTitleObject = new EmployeeToJobTitleMatchLink();
link1ToAddThruJobTitleObject.TheEmployee = emp;
link1ToAddThruJobTitleObject.TheJobTitle = jtDba;
link1ToAddThruJobTitleObject.JobStartedOnDate = DateTime.Now.AddDays(-1);
link1ToAddThruJobTitleObject.PriorityRank = 5;
jtDba.AddEmployeeLink(link1ToAddThruJobTitleObject);
jtRepo.Add(jtDba);
EmployeeToJobTitleMatchLink link1ToAddThruEmployeeObject = new EmployeeToJobTitleMatchLink();
link1ToAddThruEmployeeObject.TheEmployee = emp;
link1ToAddThruEmployeeObject.TheJobTitle = jtDev;
link1ToAddThruEmployeeObject.JobStartedOnDate = DateTime.Now.AddHours(-1);
link1ToAddThruEmployeeObject.PriorityRank = 1;
EmployeeToJobTitleMatchLink link2ToAddThruEmployeeObject = new EmployeeToJobTitleMatchLink();
link2ToAddThruEmployeeObject.TheEmployee = emp;
link2ToAddThruEmployeeObject.TheJobTitle = jtProjectManager;
link2ToAddThruEmployeeObject.JobStartedOnDate = DateTime.Now.AddYears(5);
link2ToAddThruEmployeeObject.PriorityRank = 2;
emp.AddJobTitleLink(link1ToAddThruEmployeeObject);
emp.AddJobTitleLink(link2ToAddThruEmployeeObject);
empRepo.Upsert(emp);
【讨论】:
和@granadaCoder 如何正确插入这些实体?例如,您能否给我看一个保存候选人和 2 个电话的命令示例? 我附上了答案。现在我记得一些关于让所有对象排队的巫术......当您将中间对象添加到任何一个外部对象时。请参阅上面的添加和删除“链接”方法。 嗨@granadaCoder...非常感谢您的帮助。关于您的代码的一个问题...您的 EmployeeControlles 和 JobTitleController 是否有 NHibernate Add Method ? 该代码是非常简单的“NHibernate”代码。例如: public void Add(JobTitleNHEntity jt) using (ISession session = ISessionCreator.OpenSession()) using (ITransaction transaction = session.BeginTransaction()) session.Save(jt);事务.Commit(); 也许你可以从这里得到一些提示:marekblotny.blogspot.com/2009/02/…以上是关于如何使用 FluentNHibernate N:N 映射与附加属性的关系的主要内容,如果未能解决你的问题,请参考以下文章
FluentNHibernate - 如何在没有任何身份字段的情况下进行映射
使用 FluentNhibernate 和 Autofac 的映射接口
Fluent nHibernate - 如何在联结表上映射非键列?
使用FluentNHibernate引用的CustomType