NHibernate中的子表

时间:2009-02-16 09:31:03

标签: 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; }
    public virtual string Name { get; set; }
}

并且不必在Person上创建“特殊”AddPet方法以保存Child宠物。

public void AddPet(Pet p)
{
    p.Person = this;
    Pets.Add(p);
}

_session.SaveOrUpdate(person);

不保存宠物,因为宠物没有人参考。

如果我更新宠物以包含此参考。

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())

我能想到的唯一其他选项是自定义列表,用于在添加子实体时设置父引用。

4 个答案:

答案 0 :(得分:3)

我稍微修改了你的例子(与这里的许多建议一致):

public class Person
{
    private IList<Pet> pets;

    protected Person()
    {}

    public Person(string name)
    {
        Name = name;
        pets = new List<Pet>();
    }

    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IEnumerable<Pet> Pets
    {
        get { return pets; }
    }

    public virtual void AddPet(Pet pet)
    {
        pets.Add(pet);           
    }
}

public class Pet 
{
    protected Pet()
    {}

    public Pet(string name)
    {
        Name = name;
    }

    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();
        Map(x => x.Name);
        HasMany(x => x.Pets).Cascade.AllDeleteOrphan().Access.AsLowerCaseField();
    }
}

public class PetMap : ClassMap<Pet>
{
    public PetMap()
    {
        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);
    }

通过。

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

答案 1 :(得分:0)

C# Reflection & Generics

可以使用这种方法吗?

不适合孩子的孩子,但对父母子女来说非常好。

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);
    }
}

答案 2 :(得分:0)

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

我会包含

public void AddPet(Pet p)

在Person对象中并有一个

IEnumerable<Pets> Pets { get; }

公共财产。 List属性将受到保护。这样,person.Pets.Add(新的Pet())就不会发生,你就安全了。

答案 3 :(得分:0)

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

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

顺便说一句,NHibernate可能存储了宠物,但没有信息来写正确的外键。所以它存储了从未出现在任何人的宠物清单中的孤儿。