C# NHibernate中的子表

C# NHibernate中的子表,c#,nhibernate,C#,Nhibernate,在NHibernate中是否有任何方法可以使用以下实体 public class Person { public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual IList<Pet> Pets { get; set; } } public class Pet { public virtual int Id { get; set;

在NHibernate中是否有任何方法可以使用以下实体

public class Person
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Pet> Pets { get; set; }
}

public class Pet
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}
不保存宠物,因为宠物没有个人参考

public class Pet
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Person Person { get; set; }
}
如果我更新宠物以包含此引用

public class Pet
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Person Person { get; set; }
}
在新宠物身上,我仍然需要安排一个人,这对我来说似乎是杀伤力过大,而且风险也很大,因为人们仍然可以打电话给我

person.Pets.Add(new Pet())
我唯一能想到的另一个选项是自定义列表,它在添加子实体时设置父引用。

也许可以使用这种方法

这对孩子的孩子不起作用,但对父母的孩子却很好

protected static void SetChildReferences(E parent)
{
    foreach (var prop in typeof(E).GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        if (!prop.CanRead) continue;

        Type listType = null;
        foreach (Type type in prop.PropertyType.GetInterfaces())
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                listType = type.GetGenericArguments()[0];
                break;
            }
        }

        List<PropertyInfo> propsToSet = new List<PropertyInfo>();
        foreach (PropertyInfo childProp in 
            (listType ?? prop.PropertyType).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (childProp.PropertyType == typeof(E))
                propsToSet.Add(childProp);
        }

        if (propsToSet.Count == 0) continue;

        if (listType == null)
        {
            object child = prop.GetValue(parent, null);
            if (child == null) continue;
            UpdateProperties(propsToSet, child, parent);
        }
        else
        {
            ICollection collection = (ICollection)prop.GetValue(parent, null);
            foreach (object child in collection)
            {
                if (child == null) continue;
                UpdateProperties(propsToSet, child, parent);
            }
        }
    }
}

protected static void UpdateProperties(List<PropertyInfo> properties, object target, object value)
{
    foreach (PropertyInfo property in properties)
    {
        property.SetValue(target, value, null);
    }
}
受保护的静态void SetChildReferences(E父级)
{
foreach(typeof(E).GetProperties中的var prop(BindingFlags.Public | BindingFlags.Instance))
{
如果(!prop.CanRead)继续;
类型listType=null;
foreach(prop.PropertyType.GetInterfaces()中的类型)
{
如果(type.IsGenericType&&
type.GetGenericTypeDefinition()==typeof(ICollection))
{
listType=type.GetGenericArguments()[0];
打破
}
}
List propsToSet=新列表();
foreach(属性信息中的childProp)
(listType??prop.PropertyType).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if(childProp.PropertyType==typeof(E))
propsToSet.Add(childProp);
}
如果(propsToSet.Count==0)继续;
if(listType==null)
{
对象子对象=prop.GetValue(父对象,null);
如果(child==null)继续;
更新属性(propsToSet、子级、父级);
}
其他的
{
ICollection collection=(ICollection)prop.GetValue(父级,null);
foreach(集合中的对象子对象)
{
如果(child==null)继续;
更新属性(propsToSet、子级、父级);
}
}
}
}
受保护的静态void UpdateProperties(列表属性、对象目标、对象值)
{
foreach(属性中的PropertyInfo属性)
{
SetValue(target,value,null);
}
}

如果不存储关系,您如何知道一个人在数据库中有哪些宠物

我会包括

public void AddPet(Pet p)
在Person对象中,有一个

IEnumerable<Pets> Pets { get; }
IEnumerable宠物{get;}

公共财产。列表属性将受到保护。这样,person.Pets.Add(new Pet())就不会发生,您就安全了。

这不是NHibernate问题,而是域模型问题。NHibernate不负责建立您的域的引用和反向引用

我同意gcores的观点,将列表设置为受保护的或私有的。找到一种方法以一致的方式将对象链接在一起。如果它不像实现Add方法那样简单,请考虑一个单独的服务类或工厂。(如果它更复杂,请尝试类似Spring.Net的东西。)


顺便说一句,NHibernate可能存储了宠物,但没有写入正确外键的信息。因此,它存储了从未出现在任何人的宠物列表中的孤儿宠物。

我只是稍微修改了您的示例(与这里的许多建议一致):

公共类人物
{
私人宠物;
受保护人()
{}
公众人物(字符串名称)
{
名称=名称;
pets=新列表();
}
公共虚拟Guid Id{get;set;}
公共虚拟字符串名称{get;set;}
公共虚拟宠物
{
获取{返回宠物;}
}
公共虚拟空间添加宠物(宠物宠物)
{
宠物。添加(宠物);
}
}
公营宠物
{
受保护宠物()
{}
公共宠物(字符串名称)
{
名称=名称;
}
公共虚拟Guid Id{get;set;}
公共虚拟字符串名称{get;set;}
}
公共类PersonMap:ClassMap
{
公众人物地图()
{
Id(x=>x.Id).GeneratedBy.GuidComb();
Map(x=>x.Name);
HasMany(x=>x.Pets).Cascade.AllDeleteOrphan().Access.AsLowerCaseField();
}
}
公共类PetMap:ClassMap
{
公共地图()
{
Id(x=>x.Id).GeneratedBy.GuidComb();
Map(x=>x.Name);
}
}
以下测试:

    [Test]
    public void CanSaveAndRetrievePetAttachedToPerson()
    {
        Person person = new Person("Joe");
        person.AddPet(new Pet("Fido"));

        Session.Save(person);

        Person retrievedPerson = Session.Get<Person>(person.Id);
        Assert.AreEqual("Fido", retrievedPerson.Pets.First().Name);
    }
[测试]
public void可以保存和检索petattachedtoperson()
{
人员=新人员(“乔”);
person.AddPet(新宠物(“Fido”));
会话。保存(个人);
Person retrievedPerson=Session.Get(Person.Id);
Assert.AreEqual(“Fido”,retrievedPerson.Pets.First().Name);
}
通行证


请注意,这是使用Fluent NHibernate进行映射和会话。

是的,我知道这是一种方式,我的问题是宠物是否在一个人的宠物集合中推断出这种关系?不,我没有,因为它没有关系。我的意思是,如果你重新加载一个人,你不能重新加载这个人的宠物,因为没有办法知道哪些宠物会属于那个人。这就是为什么你需要一个人的宠物财产。这不是NHibernate的问题,这是DB的问题。仔细考虑后,我同意你和其他人的看法,这是一个领域问题,而不是NHibernate的问题。
    [Test]
    public void CanSaveAndRetrievePetAttachedToPerson()
    {
        Person person = new Person("Joe");
        person.AddPet(new Pet("Fido"));

        Session.Save(person);

        Person retrievedPerson = Session.Get<Person>(person.Id);
        Assert.AreEqual("Fido", retrievedPerson.Pets.First().Name);
    }