【问题标题】:Table-per-subclass fluent nhibernate not working每个子类的表流利的休眠不工作
【发布时间】:2015-09-13 16:48:43
【问题描述】:

我定义了以下类:

还有我数据库中的这些表:

我流畅的 NHibernate 映射是:

public class BusinessUnitMap : ClassMap<BusinessUnit>
{
    public BusinessUnitMap()
    {
        Table("BusinessUnits");

        Id(x => x.Id);

        Map(x => x.Code);
        Map(x => x.Name);
        Map(x => x.ParentId);
        Map(x => x.Type).Column("Type").CustomType<BusinessUnitType>();
    }
}

public class CompanyMap : SubclassMap<Company>
{
    public CompanyMap()
    {
        Table("CompanyData");

        KeyColumn("BusinessUnitID");

        Map(x => x.Something);
    }
}

public class FranchiseeMap : SubclassMap<Franchisee>
{
    public FranchiseeMap()
    {
        Table("FranchiseeData");

        KeyColumn("BusinessUnitID");

        Map(x => x.SomethingDifferent);
    }
}

public class StoreMap : SubclassMap<Store>
{
    public StoreMap()
    {
        Table("StoreData");

        KeyColumn("BusinessUnitID");

        Map(x => x.SomethingElse);
    }
}

问题 #1 据我所知,我的代码和数据库的设置与我能找到的每个示例相同。根据这些文章,NHibernate 应该足够聪明,可以在我查询特定子类时确定要实例化哪个子类。但是,当我执行以下语句时:

var result = Session.QueryOver<BusinessUnit>()
                    .Where(x => x.Code == "Acme")
                    .SingleOrDefault();

因为无法创建抽象 BusinessUnit 类的实例而引发异常。我可以让它工作的唯一方法是将 Company 指定为 QueryOver 的类型参数。

我已经确认使用鉴别器会中断,因为 NHibernate 正在寻找存在于单个表中的所有列。但是,如果没有它,我很难知道 NHibernate 将如何知道要实例化什么类型。

我做错了什么?是我的映射问题,我查询的方式,...?

问题 #2 当我将查询更改为以下内容时:

public T WithCode<T>(String code)
    where T : BusinessUnit
{
    var result = Session.QueryOver<T>()
                        .Where(x => x.Code == code)
                        .SingleOrDefault();

    return result;
}

我得到一个异常,表明 UPDATE 语句与外键约束冲突。更新声明!!!!显然有些事情仍然不对劲。 QueryOver 调用如何导致 UPDATE 语句?我错过了什么?

【问题讨论】:

  • NHibernate 使用其中一个子表中的存在性作为要实例化哪个类的指示符。看起来有一个 acme 条目在其他表之一中没有相应的条目
  • 不知道你是怎么得到的。 “Acme”存在于 BusinessUnits 和 CompanyData 表中。
  • 我重现了您描述的错误,其中基表中的条目和子表中的条目丢失。也许还有其他情况会出现相同的错误
  • 检查更新语句以查看错误可能来自何处。也许您没有发布错误来源的类
  • 在改用@filo 的方法并向后工作后,我终于弄清楚了我最初的实现出了什么问题。 ParentId 属性是一个 Guid,但其中一条记录在数据库中为空。 NHibernate 不是因为 Guid 值(不是)而抛出异常,而是认为属性是脏的并尝试更新记录!只有通过对日志的深入分析,我才发现表明它认为 ParentId 是脏的语句。我碰巧是因为 null 值而使属性 Guid?而且,瞧!一切正常。

标签: nhibernate fluent-nhibernate fluent-nhibernate-mapping queryover


【解决方案1】:

您的数据似乎不一致。使用可选的判别器映射可能会更好。如果您真的不需要代码中的 BusinessUnitType 属性,那么只需删除该属性周围的所有内容Type

public enum BusinessUnitType
{
    Company,
    Franchisee
}

public abstract class BusinessUnit
{
    public virtual int Id { get; set; }
    public virtual string Code { get; set; }
    public virtual string Name { get; set; }
    public virtual BusinessUnit Parent { get; set; }
    public abstract BusinessUnitType Type { get; }
}

public class Company : BusinessUnit
{
    public virtual string Something { get; set; }

    public override BusinessUnitType Type { get { return BusinessUnitType.Company; } }
}

public class Franchisee : BusinessUnit
{
    public virtual string SomethingDifferent { get; set; }

    public override BusinessUnitType Type { get { return BusinessUnitType.Franchisee; } }
}

public class BusinessUnitMap : ClassMap<BusinessUnit>
{
    public BusinessUnitMap()
    {
        Table("BusinessUnits");

        Id(x => x.Id);

        Map(x => x.Code);
        Map(x => x.Name);
        References(x => x.Parent);

        DiscriminateSubClassesOnColumn("Type");

        Map(x => x.Type, "Type")
            .Access.None()
            .CustomType<BusinessUnitType>().ReadOnly();
    }
}

public class CompanyMap : SubclassMap<StrangeTablePerSubclass.Company>
{
    public CompanyMap()
    {
        DiscriminatorValue((int)new Company().Type);

        Join("CompanyData", join =>
        {
            join.KeyColumn("BusinessUnitID");
            join.Optional();
            join.Map(x => x.Something);
        });
    }
}

public class FranchiseeMap : SubclassMap<Franchisee>
{
    public FranchiseeMap()
    {
        DiscriminatorValue((int)new Franchisee().Type);
        Join("FranchiseeData", join =>
        {
            join.KeyColumn("BusinessUnitID");
            join.Optional();
            join.Map(x => x.SomethingDifferent);
        });
    }
}

【讨论】:

  • 判别器要求所有数据都在一个表中。虽然示例很简单,但它只是一个示例,我删除了每个子类实际具有的众多属性。
  • 查看代码。虽然鉴别器在一个表中具有所有内容,但连接元素从其他表中获取属性,但可以为另一个表中没有条目的情况指定 Optional()
  • 我在运行您的代码时遇到异常,因为数据库中的 BusinessUnits.Type 列是整数,并且生成的 SQL 将其视为字符串。有任何想法吗?我已经将枚举映射到数据库中的整数,就像过去一样,没有问题 - 除了 .Access.None() 部分。
  • 仅供参考 - 当我将 Type 属性和所有相关部分更改为 Int32 而不是 BusinessUnitType 时,您的解决方案有效。
  • 我必须在 DiscriminatorValue 语句中为 (int) 添加强制转换,以便生成的 SQL 将使用 WHERE 子句中的整数值而不是使用字符串表示。
猜你喜欢
  • 2010-10-13
  • 2011-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-20
相关资源
最近更新 更多