业务对象之间的过度访问和令人难以置信的资源饥饿关系.我怎样才能解决这个问题?

Mik*_*ike 5 c# data-access business-objects

首先,这似乎是一个很长的问题.我认为不是......代码只是我目前正在做的事情的概述.这感觉不对,所以我正在寻找建设性的批评和警告,以及我能做些什么的陷阱和建议.

我有一个包含业务对象的数据库.
我需要访问父对象的属性.
我需要通过业务对象维护某种状态.

如果你看一下这些类,我认为访问修饰符是不对的.我不认为它的结构很好.大多数关系都是用公共属性建模的.SubAccount.Account.User.ID < - 所有这些都是公开的..

是否有更好的方法来建立类之间的关系而不是这样,所以它不是那么"公开"?

这个问题的另一部分是关于资源:

如果我要创建一个返回List的User.GetUserList()函数,并且我有9000个用户,当我调用GetUsers方法时,它将生成9000个User对象,并且内部将生成9000个新的AccountCollection对象.我该怎么做才能使这个项目不那么资源匮乏?

请找到下面的代码并将其撕成碎片.

public class User {

   public string ID {get;set;}
   public string FirstName {get; set;}
   public string LastName {get; set;}
   public string PhoneNo {get; set;}

  public AccountCollection accounts {get; set;}

  public User {
     accounts = new AccountCollection(this);
  }

  public static List<Users> GetUsers() {
     return Data.GetUsers();
  }

}

public AccountCollection : IEnumerable<Account> {
  private User user;

  public AccountCollection(User user) {
     this.user = user;
  }

  public IEnumerable<Account> GetEnumerator() {
     return Data.GetAccounts(user);
  }
}


public class Account {

   public User User {get; set;}  //This is public so that the subaccount can access its Account's User's ID
   public int ID;
   public string Name;

   public Account(User user) {
      this.user = user;
   }

}

public SubAccountCollection : IEnumerable<SubAccount> {
  public Account account {get; set;}

  public SubAccountCollection(Account account) {
     this.account = account;
  }

  public IEnumerable<SubAccount> GetEnumerator() {
     return Data.GetSubAccounts(account);
  }
}


public class SubAccount {
   public Account account {get; set;}    //this is public so that my Data class can access the account, to get the account's user's ID.

   public SubAccount(Account account) {
      this.account = account;  
   }

   public Report GenerateReport() {
       Data.GetReport(this);
   }

}


public static class Data {

  public static List<Account> GetSubAccounts(Account account) {

      using (var dc = new databaseDataContext()) {
          List<SubAccount> query = (from a in dc.Accounts
                                where a.UserID == account.User.ID  //this is getting the account's user's ID
                                select new SubAccount(account) {
                                    ID = a.ID,
                                    Name = a.Name,
                                }).ToList();
      }

  }

  public static List<Account> GetAccounts(User user) {

     using (var dc = new databaseDataContext()) {
         List<Account> query = (from a in dc.Accounts
                               where a.UserID == User.ID  //this is getting the user's ID
                               select new Account(user) {
                                   ID = a.ID,
                                   Name = a.Name,
                               }).ToList();
     }
  }

  public static Report GetReport(SubAccount subAccount) {

     Report report = new Report();
     //database access code here
     //need to get the user id of the subaccount's account for data querying.
     //i've got the subaccount, but how should i get the user id.
     //i would imagine something like this:
     int accountID = subAccount.Account.User.ID;
     //but this would require the subaccount's Account property to be public.
     //i do not want this to be accessible from my other project (UI).
     //reading up on internal seems to do the trick, but within my code it still feels
     //public. I could restrict the property to read, and only private set.

     return report;
  }

  public static List<User> GetUsers() {

     using (var dc = new databaseDataContext()) {
         var query = (from u in dc.Users
                     select new User {
                       ID = u.ID,
                       FirstName = u.FirstName,
                       LastName = u.LastName,
                       PhoneNo = u.PhoneNo
                     }).ToList();

         return query;
     }
  }

}
Run Code Online (Sandbox Code Playgroud)

Dav*_*all 3

这个答案最终包含了很多流行语标题。希望我能解释每一项以及为什么它适用于此。我认为我下面介绍的每个概念都值得考虑 - 它们并不总是适用,但我发现它们都是我个人在考虑系统结构时认为有价值的东西。

单一职责

首先考虑每个对象的职责 - 它的工作是什么?一般来说,一旦您决定为每个类别指定一个作业,您就会找到更好的设计。目前,您的许多类做得太多了,保留了真正应该作为服务存在的逻辑。

上面的第一个示例是您的 User 类:

public class User { 

   public string ID {get;set;} 
   public string FirstName {get; set;} 
   public string LastName {get; set;} 
   public string PhoneNo {get; set;} 

  public AccountCollection accounts {get; set;} 

  public User { 
     accounts = new AccountCollection(this); 
  } 

  public static List<Users> GetUsers() { 
     return Data.GetUsers(); 
  } 

} 
Run Code Online (Sandbox Code Playgroud)

为什么要提供从数据源检索用户的方法呢?该功能应该移出到用户服务中。

另一个关键示例是 SubAccount 上的GenerateReport 方法 - 不要将报告生成逻辑与 SubAccount 对象紧密联系在一起。将其拆分将为您提供更大的灵活性,并减少子帐户更改破坏报告逻辑的情况。

延迟加载

再次查看您的 User 类 - 为什么它在实例化时加载所有用户帐户?每次与用户一起工作时是否总是会使用这些对象?

通常最好引入延迟加载 - 仅在需要时检索帐户。当然,有时您需要预先加载(如果您知道您很快就会需要该对象,因此希望减少数据库访问),但您应该能够针对这些异常进行设计。

依赖注入

这种情况是从延迟加载点和单一责任点开始的。您有很多对数据类等内容的硬编码引用。这使您的设计更加严格 - 重构数据访问以引入延迟加载,或者更改用户记录的检索方式变得更加困难,因为许多类都直接访问数据访问逻辑。

摘要对象

向 Cade Roux 致敬 - 我从未听说过 Digest 对象这个术语,通常将它们称为轻量级 DTO。

正如 Cade 所说,如果您所做的只是显示绑定到唯一 id 的用户名组合框,则没有理由检索包含功能齐全的用户对象的丰富列表。

引入一个轻量级的用户对象,它只存储非常基本的用户信息。

这又是引入服务/存储库抽象和某种依赖注入的另一个原因。当您将数据检索与实际对象封装在一起并且没有与数据访问实现紧密绑定时,更改从数据存储中检索的对象类型会变得更加容易。

德墨忒耳定律

您的对象对彼此的内部结构了解太多。允许从用户深入到帐户,然后再深入到子帐户会混淆每个对象的职责。您可以从用户对象设置子帐户信息,但实际上您不应该这样做。

我总是与这个原则作斗争,因为钻探层次结构似乎非常方便。问题是它会阻止你仔细思考每个对象的封装和角色。

也许不要向用户公开您的 Account 对象 - 而是尝试引入公开 Account 对象的相关成员的属性和方法。查看 GetAccount 方法而不是 Account 属性,以便您强制自己使用帐户对象而不是将其视为 User 的属性。