【问题标题】:How to set ID in Object Oriented Code如何在面向对象代码中设置 ID
【发布时间】:2009-10-18 16:53:30
【问题描述】:

当谈到面向对象的编程时,我有点困惑,比如 3 层应用程序。这只是我正在尝试做的一个小例子(我将缩短数据库设计以使其简单)。假设我正在制作票务服务台系统。工单有描述、负责人、到期日,当然还有 ID(唯一)。

假设 ID 只是一个 IDENTITY 列,它是整数类型并自动获取其值 (SQL Server)。也就是说,它只有在插入完成后才会获得它的值。

现在只是一些示例伪代码(它可能不正确,所以不要让语法错误,只是试图获得有关如何存储 ID 的答案)。

我可以轻松创建一个 Ticket 类

public class Ticket
 {
   private string m_description;
   private date m_duedate;
   private string m_responsible;
   private int m_id; //should this be read only ??? or...how

  //then I can have a bunch of Properties to get / set these private variables
   public Property Responsible{
       get 
           { return m_responsible; }
       set 
           { m_responsible = value; }
   }

  //and so on..again dont worry about syntax.  Now should I have a get / set for the ID?
 }

好的,所以我有一个名为ticket的类..但是当我创建一个ticket对象并需要从我的BLL(业务逻辑层)插入它时会发生什么

Bll b = new Bll();
  Ticket t = new Ticket();
  t.Responsible = someString;
  t.DueDate = someDate;
  t.Description = someLongString;
  //use the BLL to create a ticket object and pass it to the DAL ?
  //then assign the ID from the database to t.ID ???
  t.ID = bll.InsertTicket(t);

//就是把它传递给BLL,BLL做它的事情,然后把它传递给DAL做一个INSERT语句,然后返回数据库给它的ID号。

所以我的问题是我需要如何或何时分配 t.ID,或者我什至需要在插入完成后给出。我总是对 OOP 感到困惑,因为我倾向于认为它使事情变得更复杂,而不是仅仅传递一大堆参数。

好的,在有人可以帮助我了解我是否需要获取/设置 ID 以及是否应该将该值传递回我的界面之后。我的第二个问题是更新呢? 假设最终用户找到了一张票,所以在我的界面上我检索了一些票数据并且有人想要更新说描述和截止日期。当我“提交”这些更改时,我是否应该只创建一个票证对象,设置所有 get/set 属性值,仅此而已?还是我应该将 ID 号和所有参数传递给我的 BLL 并让它处理这一切?

希望这一切都有意义!

非常感谢大家!

【问题讨论】:

  • @Peter Lillevold 的编辑:“orientated”是不太常用的英式拼写;这不是错误。

标签: .net oop design-patterns


【解决方案1】:

答案是它取决于您使用的框架或库。

并非所有 BLL 都允许您简单地说 Ticket t = new Ticket(),您可能必须使用 t = b.CreateTicket() 或其他东西。 Id 可以是一个临时值,也可以只是 null/0,直到它被持久化。

关于你问题的第二部分:

如果您想更新票证,您当然不要创建新的票证对象。您更新现有对象。

也许您应该根据特定的库或技术来澄清或重新提出问题。

【讨论】:

  • 对不起,这没有意义。为什么要从业务逻辑层创建票证?对我来说,您只需创建票证对象,然后将其传递给 BLL 以创建票证。不管你在什么框架。只是提醒一下,当我说 Ticket t = new Ticket();我肯定不在代码的 BLL 中。无论是Web界面还是客户端界面,我都在结束程序。我实例化票证类型的对象。设置其属性,然后将其传递给业务逻辑层以创建票证。但我的问题仍然没有得到回答。身份证?
  • 我说过一些库不会让你直接创建对象。其他人会。限制它的一个原因是 BLL 或 DAL 想要创建 ID。
  • Ok Henk 假设这是 C#。我想我的问题是,我的逻辑是否正常。创建一个名为 ID 的读/写属性是否安全?然后我实例化一个票据对象,设置它的属性,并将它作为票据对象传递给 BLL。反过来,BLL 将票证对象传递给我的 DAL。我的 DAL 访问票证对象的属性并将票证插入 SQL 表并将 IDentifier 返回给 BLL,BLL 作为回报将其返回给我的客户端界面。这个返回值可以是 t.ID = b.CreateTicket(t)。这意味着它会将值从 db 分配给 t.ID 属性。可以吗?
【解决方案2】:

通常我会使用某个键值(即 -1)作为默认的未保存 ID。 然后将其向上传递到将保存此对象的相关数据库层 并更新 ID 并在必要时将其返回(否则,如果需要,终端客户端请求更新)。 我经常在构造对象时将键默认为默认值。

在以前的一些项目中,我使用 Guid 作为键,因为它们可以在客户端/应用程序服务器上生成,而无需数据库来生成键(以及随后更新父对象的外键)。这可以在执行 SQL 方面节省大量时间,因为它可以更容易地进行批处理)。如果需要,可以使用 Guid Comb 更快地生成密钥。但是,如果您需要增量密钥,这可能不是一个选项。

这可能因框架/技术/基础设施而异...

【讨论】:

  • 好的,我明白了,假设我可以创建一个名为 ID 的读/写属性说是否安全。一旦我做了这样的事情:ticket t = new Ticket();默认构造函数可以将此 ID 设置为 -1。然后我填充其余的属性,并将其作为票证对象参数传递给业务逻辑层?然后在我的 BLL 内部,我可以将它作为票证对象参数传递给我的数据库层吗?我可以将相同的票证对象从客户端代码传递到 BLL 到 DAL。那么我将访问属性并通过 sproc 将票证作为记录插入并返回 ID?
【解决方案3】:

使用 NHibernate 或其他一些不错的 O/R Mapper,你甚至不会遇到这个问题。该属性可以是只读的,并且可以在插入之前或之后由 ORM 本身设置。除此之外,如果您是手动操作,则可以将其设为只读,并在需要时使用少量反射从 DAL 设置 ID,而不会违反封装。

顺便说一句,为什么要将对象传递给 BLL?对象本身不应该实现自己的业务逻辑吗?这就是制作对象的意义所在。否则,您应该只使用强类型数据集。听起来你在做“贫血的领域模型”,这很糟糕:

http://martinfowler.com/bliki/AnemicDomainModel.html

【讨论】:

  • 我怎样才能将此票证对象提供给 DAL(数据访问层)。该接口无法访问DAL,只能通过BLL
【解决方案4】:

也许这份清单会对您有所帮助:

    public class Ticket : IEquatable<Ticket>
    {

     public static Ticket Create()
     {
           return new Ticket { Id = Guid.NewGuid() };
     }

     /// Id must be really unique
     public Guid Id {get; private set;}

     /// other properties
     /// ...

     bool IEquatable.Equals(Ticket ticket)
     {
           return ticket != null && ticket.Id == this.Id;
     }

     /// hidden ctor
     protected Ticket() {}
    }

【讨论】:

    【解决方案5】:

    你可以试试这样的。代码并不完美,但它会给你一个想法。抱歉,我正在尝试比我应该做的更快,所以它非常粗糙。

    // returns Record number of object
    public int Insert()
    {
      SqlConnection conn = new SqlConnection(dbstring);
      SqlCommand insertCommand = new SqlCommand("mystoredprocedure");
      SqlParameter newRecNo = new SqlParameter();
      newRecNo.Direction = ParameterDirection.ReturnValue;
      conn.Open();
      insertCommand.ExecuteNonQuery();  // Your sqlparameter will now contain the recno
      conn.close(); // use try/catch, etc.
      return newRecNo.value;
    }
    public static MyObject GetData(int TicketID)
    {
      // Get object data from DB.
    }
    
    In the stored procedure to insert, put this in:
    
    declare @recno int
    set @recno =  @@identity
    return @recno
    

    【讨论】:

      【解决方案6】:

      几点:

      1 -- Hungarian notation 是 1985 年的老兄。 :)

      2 -- 你在这里重新发明轮子。有很多很好的 ORM frameworks 以一种很好的标准化方式处理持久性,让您继续编写您的域。

      3 -- 好的,所以如果你绝对不能使用像 NHibernate 这样的 ORM 框架,或者,上帝保佑,实体框架,那么我建议你遵守 ORM 框架使用的标准模式之一,比如 @ 987654323@ 或DataMapper。是的,您需要在持久化新对象后分配Identity Field。 Martin Fowler 的 ActiveRecord 和 DataMapper 模式都使用“插入”方法将新对象持久保存到数据库中。在 Rails 等其他 ORM 框架中也有类似的方法,使用“save”,新对象可以通过调用“new_record?”等接口来报告它是否已被持久化。在调用“保存”或“插入”之前返回 TRUE。在调用其中一种方法之前,ID 字段通常默认为 0 或 -1,两者都是无效的 PKID。在“插入”或“保存”中,新的 id 字段被检索并分配给新对象的 id 字段。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-14
        • 2013-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-11
        相关资源
        最近更新 更多