NHibernate - 初始化列表 - 最佳实践?

6
我想了解一些CodeWarning(ConstructorsShouldNotCallBaseClassVirtualMethods),并且是否有更好的方法来处理它。我有一个简单的日志收集器类,并且使用NHibernate检索一些对象。
有时我自己创建对象并将它们添加到NHibernate进行持久化。如何确保列表永远不会为空是最佳方式?
目前我正在这样做,但它似乎不是“完美”的。对此有什么想法吗?
public class LogRun
{
    public virtual int Id { get; private set; }
    public virtual DateTime StartTime { get; set; }
    public virtual DateTime EndTime { get; set; }
    public virtual IList<Log> LogMessages { get; set; }
    public virtual int LogMessageCount { get { return LogMessages.Count; } }

    public LogRun()
    {
        LogMessages = new List<Log>();
    }


}
2个回答

8

LogMessages是一个持久化的东西吗?如果是,最好不要暴露公共的setter。如果你从数据库中检索然后用新的列表替换它,NHibernate会变得很奇怪:

var myLog = session.Get<LogRun>(1);
Assert.True(myLog.LogMessages.Count > 0);
myLog.LogMessages = new List<Log>();

如果您注意到了,NHibernate返回的是一个代理对象,将其替换为通用列表会导致在尝试保存时出现问题。
作为一条规则,我更喜欢拥有一个私有字段,我初始化它,然后只向客户端公开一个getter:
public class LogRun
{
    private IList<Log> logMessages = new List<Log>();

    public virtual int Id { get; private set; } 
    public virtual DateTime StartTime { get; set; } 
    public virtual DateTime EndTime { get; set; }
    public virtual IList<Log> LogMessages { get { return logMessages; } } 
    public virtual int LogMessageCount { get { return LogMessages.Count; } }

    public void AddLogMessage(Log log)
    {
        logMessages.Add(log);
    }
}

实际上,我更进一步,客户端会得到一个IEnumerable<>,而我会添加一个帮助函数进行添加。

我的实现看起来像:

public class LogRun
{
    private IList<Log> logMessages = new List<Log>();

    public virtual int Id { get; private set; } 
    public virtual DateTime StartTime { get; set; } 
    public virtual DateTime EndTime { get; set; }
    public virtual IEnumerable<Log> LogMessages { get { return logMessages; } } 
    public virtual int LogMessageCount { get { return LogMessages.Count(); } }

    public void AddLogMessage(Log log)
    {
        logMessages.Add(log);
    }
}

2
我遵循相同的模式,只是我将所有初始化工作都放在构造函数中完成。此外,在add方法中通常需要设置对父级的引用,即logMessages.Add(log); log.LogRun = this; - Jamie Ide
2
我更进一步,使我的 getter 返回只读版本... 返回 logMessages.ToList<Log>().AsReadOnly(); - Webjedi
1
Jamie Ide:如果它是一个带有父级的一对多关系,那么这个助手函数就是设置它的好地方。如果是多对多,则没有父级。不确定上下文,但高度怀疑你是正确的。Webjedi,我也喜欢你的方法。这就是为什么我最终使用了Enumerable。在我的情况下,它满足了我想要对集合进行的所有操作。 - Ben
好的回答,除非您的辅助函数是虚函数,否则它无法工作。 - Xcalibur
1
请记得在映射中更改字段的访问类型(在流畅的 .Access.Field() 中或者在 XML 映射中使用 access="field")。 - falstaff

1

我也在做同样的事情,但我也想知道性能影响有多大,因为NHibernate每次默认构造函数调用时也会创建一个新的List<>..

不过我认为我们很幸运,它会起作用。考虑NHibernate创建一个延迟加载的LogRun列表(这就是为什么我们标记所有内容为virtual的原因):

  1. NHibernate将反射LogRun并创建一个派生类
  2. NHibernate将制作LogRun派生类的代理列表
  3. --
  4. 当您加载该代理时,它将实例化其中一些派生类,但首先调用基础构造函数-创建新的list<>-然后调用派生构造函数,创建代理列表。

实际上,我们已经创建了一个永远不会使用的列表。

不过考虑一下其他选择:

  • 将构造函数设置为protected,以便没有人会调用它,并提供替代方法。例如,一个静态的LogRun.GetNew();方法。
  • 允许公共set访问IList<>,并在创建新对象时自己创建它。
老实说,我认为两种方法都很凌乱,而且由于我(相当)确定每次构造函数调用时创建新的空列表的性能开销是有限的,所以这就是我个人迄今为止坚持的方法... 好吧,至少在我的分析器告诉我的之前是这样的:P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接