【问题标题】:Should the ID in the business object be read-only or not?业务对象中的 ID 是否应该是只读的?
【发布时间】:2009-08-11 10:44:35
【问题描述】:

在我看来,业务对象的 ID 字段应该是只读的(公共获取和私有集),因为根据定义,ID 永远不会改变(因为它唯一地标识了数据库中的一条记录)。

当您创建一个新对象(ID 尚未设置)时,这会产生一个问题,通过存储过程将其保存在数据库中,例如返回新创建的 ID,那么如果 ID 存在,您如何将其存储回对象中属性是只读的?

例子:

Employee employee = new Employee();  
employee.FirstName="John";  
employee.LastName="Smith";  

EmployeeDAL.Save(employee);

如果该属性是只读的,Save 方法(实际上连接到数据库以保存新员工)如何更新 Employee 对象中的 EmployeeId 属性(这应该是因为 EmployeeId 一旦创建就永远不会改变)。

看起来 Id 应该可以由 DAL 写入,而对于世界其他地方来说应该是只读的。如果 DAL 类和业务对象类位于不同的程序集中,您将如何实现这一点?

我不想在 Employee 类中创建 Save 方法,因为这个类应该与数据库无关。

【问题讨论】:

  • 一个我不相信的古老问题,有一个“不错”的答案,只是一个最不坏的妥协。
  • 安东尼,我删除了我的答案。我想我太复杂了。我同意@Martin Brown 的观点。这个问题没有简单的答案。我会创建一个新对象,即使这意味着复制所有属性。
  • @布鲁诺。我正要评论说你的答案可能会奏效。看起来这样做的最好方法是使 ID 内部化: public int Id { get;内部集;这样,只有 DAL 能够更新它并允许 DAL 更新“内部”ID 属性:[assembly: InternalsVisibleTo("DAL_Assembly")] 这样对于 DAL,内部 ID 属性将是可更新的。

标签: c# business-logic


【解决方案1】:

另一种可能的解决方案是将 Employee 声明为:-

public class Employee
{
    public int Id { get; internal set; }
}

...前提是 Employee 和 DAL 类在同一个程序集中

我不声称喜欢它,但我已经使用它了。

【讨论】:

  • 如果它们不在同一个程序集中,可以使用:[assembly: InternalsVisibleTo("DAL_Assembly")]
【解决方案2】:

你可以让你的 DAL 方法只返回一个更新的对象:

public class EmployeeDAL
{
    Employee EmployeeDAL.Save (Employee employee)
    {
        // Save employee
        // Get the newly generated ID
        // Recreate the object with the new ID and return it
    }
}

或者,您可以在代码中生成一个新 ID,使用此 ID 实例化一个对象,然后请求您的 DAL 保存它。

如果您希望在保存操作期间更新您的对象,则必须公开此属性。

我个人喜欢创建 不可变 对象,这些对象只能通过将所有值传递给构造函数来设置一次。使用这种方法,您只需创建一个要保存的对象,然后将其连同分配的 ID 从数据库中检索回来,并将其返回给调用者。

【讨论】:

  • 如果对象有 50 个属性怎么办?您将创建一个新对象,复制所有属性的值,以便更新 ID 属性。它会起作用,但对我来说似乎不合适。从数据库中检索会起作用,但这意味着您连接到数据库两次:一次用于保存对象,一次用于检索它(当您已经在客户端代码中拥有它时)。再一次,这似乎不对。
  • 如果它有 50 个属性,请使用 codeplex.com/AutoMapper 之类的东西将它们从对象 A 映射到对象 B
  • @Anthony:确实如此。这里有一个优点:通过再次从数据库中检索,您可以获得保存在那里的确切内容 - 可能是字符串被截断或其他内容。如果您在代码中出错,您在应用程序中会相信您现在拥有这组数据,而实际上您没有。但同样,这些是各种设计方法。
  • 如果我们在 .NET 中像在 C++ 中一样拥有“朋友”的概念,在这种情况下会有什么帮助。那么你的问题就很容易解决了。
  • @Anthony:你可以让你的存储过程直接把它还给你,而不是连接两次
【解决方案3】:

您可以将您的设置器设置为 ID 允许设置,前提是它之前尚未设置:

public class Employee
{
    private int? m_ID;

    public int? ID
    {
        get { return m_ID; }
        set
        {
            if (m_ID.HasValue())
                throw ...
            m_ID = value;
        }
    }
}

另外,我认为某些框架支持这种类型的功能(例如,我认为 NHibernate 将允许您在 ID 字段上使用私有设置器)。

【讨论】:

  • 我喜欢这个想法,但您只会意识到如果 ID 已在运行时设置,您将无法设置它。最好在编译时属性是只读的(DAL 除外)
【解决方案4】:

是否只允许 DAL 外部的代码通过不为 Id 字段(和任何其他不可变字段)提供设置器的接口引用对象:

public interface IEmployee
{
    Int32 Id {get;}
    String Name {get;set;}
    // ... and so on ...
}

public class Employee: IEmployee
{
    Int32 Id {get;set;}
    String Name {get;set;}
}

DAL 可以根据需要设置,但是消费代码不能。

【讨论】:

  • 由于您的 Employee 类是公共的(因为它应该是应该在单独程序集中的 DAL 需要访问它),没有什么可以阻止消费代码创建 Employee 类的实例(和因此从修改 ID)。
【解决方案5】:

怎么样:

Employee employee = new Employee(EmployeeDAL.GetNextID());

这也应该使您的保存代码更简单。

【讨论】:

  • 或..员工员工 = EmployeeDAL.NewEmployee();
  • 是的,我更喜欢 Andra 的解决方案。
  • 使用此解决方案,NewEmployee() 方法将立即在数据库中创建一条新记录。好处是您将拥有 EMployeeId,但如果用户最终决定不保存 Employee 怎么办?您将不得不返回数据库删除似乎不正确的记录。
  • 不会太糟糕。它甚至可以简化对数据库的并发访问。
猜你喜欢
  • 2011-05-07
  • 1970-01-01
  • 2011-02-04
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
  • 1970-01-01
相关资源
最近更新 更多