Fluent NHibernate 正确映射 readonly 属性 from base class

Fluent NHibernate Correctly map readonly property from base class

我想为我的一些实体创建一个基础 class,因为它们共享一个 Event 列表 属性。 我还想将 Event 列表 设为只读 属性.

所以我创建了一个基础 EventRelatedEntity class,然后在我的每个与事件有关的实体 class 上派生它。

另请注意 EventRelatedEntity class 没有 NHibernate 映射 class 因为它没有链接到 table.

见下面的代码。

基础class:

public class EventRelatedEntity
{
    private readonly List<Event> events;

    public virtual IReadOnlyCollection<Event> Events { get; protected set; }

    public EventRelatedEntity()
    {
        events = new List<Event>();
        Events = events.AsReadOnly();
    }

    protected virtual void AddEvent<T>(T entity, string message)
    {
        if (events == null)
            events = new List<Event>();

        Event newEvent = new Event();

        if (typeof(T) == typeof(Company))
        {
            newEvent.CompanyId = (entity as Company).Id;
            // ...and do some other stuff...
        }
        else if (typeof(T) == typeof(Document))
        {
            newEvent.DocumentId = (entity as Document).Id;
            // ...and do some other stuff...
        }
        else if (typeof(T) == typeof(Typology))
        {
            newEvent.TypologyId = (entity as Typology).Id;
            // ...and do some other stuff...
        }

        newEvent.Message = message;

        events.Add(newEvent);
    }
}

实体classes

public class Company : EventRelatedEntity
{
    [Key]
    public virtual int Id { get; protected set; }
    [Required]
    public virtual string Alias { get; set; }
    [Required]
    public virtual string CompanyName { get; set; }
    // ...and some other properties...

    #region Actions

    public virtual void AddEvent(string message)
    {
        base.AddEvent(this, message);
    }

    #endregion
}

public class Document : EventRelatedEntity
{
    [Key]
    public override int Id { get; protected set; }
    [Required]
    public virtual User User { get; protected set; }
    // ...and some other properties...

    #region Actions

    public virtual void AddEvent(string message)
    {
        base.AddEvent(this, message);
    }

    #endregion
}

// ...and some other classes...

Fluent NHibernate 映射 class实体

public class CompanyMap : ClassMap<Company>
{
    public CompanyMap()
    {
        Table("Companies");
        LazyLoad();
        Id(x => x.Id).GeneratedBy.Identity().Column("Id");
        Map(x => x.Alias).Column("Alias").Not.Nullable();
        Map(x => x.CompanyName).Column("CompanyName").Not.Nullable();
        // ...and some other mappings...

        // Link with Events table
        HasMany(x => x.Events) // Events is declared in the base class (EventRelatedEntity)
            .KeyColumn("CompanyId")
            .Access.LowerCaseField()
            .Cascade.AllDeleteOrphan();
    }
}

public class DocumentMap : ClassMap<Document>
{   
    public DocumentMap()
    {
        Table("Documents");
        LazyLoad();
        Id(x => x.Id).GeneratedBy.Identity().Column("Id");
        References(x => x.User).Column("UserId");
        // ...and some other mappings...

        // Link with Events table
        HasMany(x => x.Events) // Events is declared in the base class (EventRelatedEntity)
            .KeyColumn("DocumentId")
            .Access.LowerCaseField()
            .Cascade.AllDeleteOrphan();
    }
}

// ...and some other mapping classes...

最后我想避免直接访问 List<>.Add() 方法。我想要一个只读集合。将新的Event添加到实体的事件列表的唯一方法必须是相应实体class的AddEvent方法。

示例:

Document document = session.Get<Document>(1);
// ...the same for other derived classes...

// I WANT TO AVOID THIS!
document.Events.Add(new Event());
// WANTS TO BE THE ONLY PERMITTED WAY TO ADD NEW EVENTS
document.AddEvent("My new event message");

问题是当我执行以下操作时:

Document document = session.Get<Document>(1);

我从 NHibernate 收到一个错误:

Cannot cast objects of type 'NHibernate.Collection.Generic.PersistentGenericBag'1 [SolutionDOC_Interface.Entity.Event]' to the 'System.Collections.Generic.List'1 [SolutionDOC_Interface.Entity.Event]' type.

我认为这与 EventRelatedEntity class 没有 NHibernate 映射有关,但我无法提供映射,因为它与 table 在数据库中。 也许如果我在每个 class(公司、文档等)中声明事件列表而不使用继承,NHibernate 将工作,但这种方法会产生相当多的代码重复,我想避免。

更新 2017/10/18

按照@ryan 的建议更改代码后,现在可以使用了。

修改后的代码:

public class EventRelatedEntity
{
    private readonly IList<Event> events;

    public virtual IReadOnlyCollection<Event> Events { get; protected set; }

    public EventRelatedEntity()
    {
        events = new List<Event>();
        Events = (events as List<Event>).AsReadOnly();
    }

    // ...
}

使用列表接口而不是具体列表 class,那么 NHibernate.Collection.Generic.PersistentGenericBag 的转换应该有效。

使用 IList<Event> 而不是 List<Event> 所以 EventRelatedEntity 变成:

public class EventRelatedEntity
{
    private readonly IList<Event> events;

    // rest of implementation...
}