从数据库读取时,HasMany关系会导致"找到对集合的共享引用"

Jan*_*eer 6 nhibernate fluent-nhibernate

我是NHibernate的新手,遇到映射问题.谷歌未能给出答案.

我的实体看起来像这样:

public class Triage
{
    public virtual Guid Id { get; set; }

    public virtual IDictionary<int, Discriminator> Discriminators { get; set; }

    // This is to keep FluentNHibernate happy
    public virtual int? SelectedDiscriminatorId { get; set; }
}

public class Discriminator
{
    public virtual int Id { get; set; }
    public virtual int LanguageId { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as Discriminator;
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        return Id == other.Id && LanguageId == other.LanguageId;
    }

    public override int GetHashCode()
    {
        return new { Id, LanguageId }.GetHashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

我的映射看起来像这样:

public class TriageMap : ClassMap<Triage>
{
    public TriageMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();
        HasMany(x => x.Discriminators)
            .KeyColumn("Id")
            .PropertyRef("SelectedDiscriminatorId")
            .Inverse()
            .Cascade.All()
            .Not.LazyLoad()
            .AsMap(x => x.LanguageId);

        // This mapping is only needed to keep FluentNHibernate happy...
        Map(x => x.SelectedDiscriminatorId);
    }
}

public class DiscriminatorMap : ClassMap<Discriminator>
{
    public DiscriminatorMap()
    {
        CompositeId()
            .KeyProperty(x => x.Id)
            .KeyProperty(x => x.LanguageId);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个想法是Triage有一个选择的Discriminator(SelectedDiscriminatorId),Discriminator-table包含几种可用语言的描述文本.并不特别喜欢Triage所指的Discriminator和SelectedDiscriminatorId的构造,它只是Discriminator(Id和LanguageId)中复合键的一部分,但这就是我的数据库的外观.

所以当我像这样取得我的分数:

_sessionFactory = CreateSessionFactory();
ISession session = _sessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
var triages = _sessionFactory
    .GetCurrentSession()
    .Query<Triage>()
    .Fetch(t => t.Discriminators)
    .ToList();
session.Flush();
session.Close();
CurrentSessionContext.Unbind(_sessionFactory);
Run Code Online (Sandbox Code Playgroud)

然后一切正常,条件是SelectedDiscriminatorId在获取的分类中是唯一的.但是,当有几个具有相同SelectedDiscriminatorId的分类时,我在执行session.Flush()语句时得到一个HibernateException,说"附加信息:找到对集合的共享引用:TestProject.Triage.Discriminators".

知道这里有什么问题以及我如何纠正这个问题?谢谢.

这是数据库的外观:

CREATE TABLE [dbo].[Triage](
    [Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Triage_Id]  DEFAULT (newid()),
    [SelectedDiscriminatorId] [int] NULL,
 CONSTRAINT [PK_Triage] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[Discriminator](
    [ID] [int] NOT NULL,
    [DisplayText] [nvarchar](255) NULL,
    [LanguageID] [int] NOT NULL,
    [Description] [nvarchar](4000) NULL,
 CONSTRAINT [PK_Discriminator] PRIMARY KEY CLUSTERED 
(
    [ID] ASC,
    [LanguageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

Fir*_*iro 2

一个更理智的对象模型是

public class Triage
{
    public virtual Guid Id { get; set; }

    public virtual Discriminator SelectedDiscriminator { get; set; }

    // left out Equals and GetHashcode
}

public class Discriminator
{
    public Discriminator()
    {
        LocalizedTexts = new Dictionary<int, string>();
    }

    public virtual int Id { get; set; }
    public virtual string Name
    {
        get
        {
            switch (Id)
            {
                case 1:
                    return "Discriminator A";
                case 2:
                    return "Discriminator B";
                case 3:
                    return "Discriminator C";
                default:
                    return "Unknown";
            }
        }
    }
    public virtual IDictionary<int, LocalizedText> LocalizedTexts { get; protected set; }

    public override bool Equals(object obj)
    {
        var other = obj as Discriminator;

        return other != null && (Id == 0 ? ReferenceEquals(this, other) : Id == other.Id);
    }

    public override int GetHashCode()
    {
        return Id;
    }
}

public class LocalizedText
{
    public string DisplayName { get; set; }
    public string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

带映射

public class TriageMap : ClassMap<Triage>
{
    public TriageMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();

        References(x => x.SelectedDiscriminator, "SelectedDiscriminatorId");
    }
}

public class DiscriminatorMap : ClassMap<Discriminator>
{
    public DiscriminatorMap()
    {
        ReadOnly();
        SchemaExport.None();   // do not create Table for this. usefull for creating Schema for in memory unit-testing
        Table("Discriminators");
        Where("LanguageId = <some value all discriminators have>");
        Id(x => x.Id).GeneratedBy.Assigned();

        HasMany(x => x.LocalizedTexts)
            .Table("Discriminators")
            .KeyColumn("Id")
            .AsMap("LanguageId")
            .Component(c =>
            {
                c.Map(x => x.DisplayName);
                c.Map(x => x.Description);
            })
            .Cascade.AllDeleteOrphan()
            .Not.LazyLoad();
    }
}
Run Code Online (Sandbox Code Playgroud)

唯一的缺点是 sql 看起来有点奇怪,因为 NHibernate 认为鉴别器将单独存在。