9

Để bắt đầu, không, tôi không sử dụng ORM, cũng không được phép. Tôi phải cuộn các kho lưu trữ của mình bằng ADO.NET.Làm thế nào để phá vỡ phụ thuộc vòng tròn giữa các kho

Tôi có hai đối tượng:

public class Firm 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public virtual IEnumerable<User> Users { get; set; } 
} 

public class User 
{ 
    public Guid Id { get; set; } 
    public string Username { get; set; } 
    public Firm Firm { get; set; } 
} 

lưu ý các tài liệu tham khảo với nhau, một Công ty có một danh sách người dùng, mỗi tài khoản chỉ có một Công ty.

Bây giờ tôi muốn thiết kế kho của tôi:

public interface IFirmRepository 
{ 
    IEnumerable<Firm> FindAll(); 
    Firm FindById(Guid id); 
} 

public interface IUserRepository 
{ 
    IEnumerable<User> FindAll(); 
    IEnumerable<User> FindByFirmId(Guid firmId); 
    User FindById(Guid id); 
} 

Cho đến nay, như vậy tốt. Tôi muốn tải Công ty cho từng Người dùng từ UserRepository của tôi. FirmRepository biết cách tạo một Công ty từ sự kiên trì, vì vậy tôi muốn ot giữ kiến ​​thức đó với FirmRepository.

public class UserRepository : IUserRepository 
{ 
    private IFirmRepository _firmRepository; 

    public UserRepository(IFirmRepository firmRepository) 
    { 
     _firmRepository = firmRepository; 
    } 

    public User FindById(Guid id) 
    { 
     User user = null; 
     using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      SqlCommand command = connection.CreateCommand(); 
      command.CommandType = CommandType.Text; 
      command.CommandText = "select id, username, firm_id from users where u.id = @ID"; 
      SqlParameter userIDParam = new SqlParameter("@ID", id); 
      command.Parameters.Add(userIDParam); 
      connection.Open(); 
      using (SqlDataReader reader = command.ExecuteReader()) 
      { 
       if (reader.HasRows) 
       { 
        user = CreateListOfUsersFrom(reader)[0]; 
       } 
      } 
     } 
     return user; 
    } 

    private IList<User> CreateListOfUsersFrom(SqlDataReader dr) 
    { 
     IList<User> users = new List<User>(); 
     while (dr.Read()) 
     { 
      User user = new User(); 
      user.Id = (Guid)dr["id"]; 
      user.Username = (string)dr["username"]; 
      //use the injected FirmRepository to create the Firm for each instance of a User being created 
      user.Firm = _firmRepository.FindById((Guid)dr["firm_id"]); 
     } 
     dr.Close(); 
     return users; 
    } 

} 

bây giờ khi tôi tải bất kỳ Người dùng nào qua UserRepository, tôi có thể yêu cầu FirmRepository xây dựng Công ty của người dùng cho tôi. Cho đến nay, không có gì quá điên rồ ở đây.

Bây giờ là vấn đề.

Tôi muốn tải danh sách Người dùng từ FirmRepository của mình. UserRepository biết cách tạo User từ persistence, vì vậy tôi muốn giữ kiến ​​thức đó với UserRepository. Vì vậy, tôi chuyển một tham chiếu đến IUserRepository tới FirmRepository:

public class FirmRepository : IFirmRepository 
{ 
    private IUserRepository 
    public FirmRepository(IUserRepository userRepository) 
    { 

    } 
} 

Nhưng bây giờ chúng ta có vấn đề. FirmRepository phụ thuộc vào một cá thể của IUserRepository, và UserRepository bây giờ phụ thuộc vào một cá thể của IFirmRepository. Vì vậy, một kho lưu trữ không thể được tạo ra mà không có một thể hiện của người khác.

Nếu tôi giữ hộp chứa IoC OUT phương trình này (và tôi nên, b/c Bài kiểm tra đơn vị không nên sử dụng các thùng chứa IoC), không có cách nào để tôi hoàn thành những gì tôi đang cố gắng làm.

Không có vấn đề, tuy nhiên, tôi sẽ chỉ tạo FirmProxy để tải bộ sưu tập Người dùng khỏi Công ty một cách lười biếng! Đó là một ý tưởng tốt hơn, b/c Tôi không muốn tải TẤT CẢ những người sử dụng tất cả thời gian khi tôi đi để có được một công ty hoặc một danh sách các công ty.

public class FirmProxy : Firm 
{ 
    private IUserRepository _userRepository; 
    private bool _haveLoadedUsers = false; 
    private IEnumerable<User> _users = new List<User>(); 

    public FirmProxy(IUserRepository userRepository) 
     : base() 
    { 
     _userRepository = userRepository; 
    } 

    public bool HaveLoadedUser() 
    { 
     return _haveLoadedUsers; 
    } 

    public override IEnumerable<User> Users 
    { 
     get 
     { 
      if (!HaveLoadedUser()) 
      { 
       _users = _userRepository.FindByFirmId(base.Id); 
       _haveLoadedUsers = true; 
      } 
      return _users; 
     } 
    } 

} 

Vì vậy, bây giờ tôi có một đối tượng proxy tốt để tạo điều kiện tải lười. Vì vậy, khi tôi đi để tạo ra một công ty trong FirmRepository từ kiên trì, tôi thay vì trả lại một FirmProxy.

public class FirmRepository : IFirmRepository 
{ 

    public Firm FindById(Guid id) 
    { 
     Firm firm = null; 
     using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      SqlCommand command = connection.CreateCommand(); 
      command.CommandType = CommandType.Text; 
      command.CommandText = "select id, name from firm where id = @ID"; 
      SqlParameter firmIDParam = new SqlParameter("@ID", id); 
      command.Parameters.Add(firmIDParam); 
      connection.Open(); 
      using (SqlDataReader reader = command.ExecuteReader()) 
      { 
       if (reader.HasRows) 
       { 
        firm = CreateListOfFirmsFrom(reader)[0]; 
       } 
      } 
     } 
     return firm; 
    } 

private IList<Firm> CreateListOfFirmsFrom(SqlDataReader dr) 
{ 
    IList<FirmProxy> firms = new List<FirmProxy>([need an IUserRepository instance here!!!!]); 
    while (dr.Read()) 
    { 

    } 
    dr.Close(); 
    return firms; 
} 

Nhưng điều này vẫn không hoạt động !!!

Để trả lại FirmProxy thay vì Công ty, tôi cần có khả năng tạo FirmProxy mới trong lớp FirmRepository của mình. Vâng, FirmProxy lấy một cá thể IUserRepository b/c UserRepository chứa các kiến ​​thức về cách tạo một đối tượng User từ persistence. Do FirmProxy cần một IUserRepository, FirmRepository của tôi bây giờ cũng cần một IUserRepository, và tôi quay trở lại hình vuông!

Vì vậy, với mã dài hơi-giải thích/nguồn này, làm thế nào tôi có thể đi về việc có thể để tạo ra một thể hiện của một người dùng từ FirmRepository và một thể hiện của Công ty từ UserRepository không:

  1. đặt mã tạo User trong FirmRepository. Tôi không thích điều này.Tại sao FirmRepository nên biết bất cứ điều gì về việc tạo ra một cá thể của một người dùng? Điều này với tôi là một sự vi phạm của SoC.
  2. Không sử dụng mẫu Bộ định vị dịch vụ. Nếu tôi đi tuyến đường này, tôi cảm thấy điều này rất khó kiểm tra. Bên cạnh đó, các nhà xây dựng của các đối tượng có sự phụ thuộc rõ ràng làm cho những phụ thuộc đó rõ ràng.
  3. tiêm tài sản thay vì tiêm xây dựng. Điều này không khắc phục được vấn đề gì, tôi vẫn cần một cá thể của IUserRepository khi khởi tạo FirmProxy bất kể sự phụ thuộc được tiêm vào FirmProxy như thế nào.
  4. Phải "ngừng hoạt động" đối tượng Công ty hoặc Người dùng và hiển thị FirmID trên Người dùng, ví dụ thay vì Công ty. Nếu tôi chỉ làm id, thì yêu cầu tải Công ty từ UserRepository sẽ biến mất, nhưng với sự phong phú của việc có thể yêu cầu đối tượng Công ty cho bất kỳ thứ gì với bối cảnh của một cá thể Người dùng cụ thể.
  5. Đặt chỗ đến ORM. Một lần nữa, tôi muốn làm điều đó, nhưng tôi không thể. Không có ORM. Đó là quy tắc (và có, đó là quy tắc crappy)
  6. giữ tất cả các phụ thuộc có thể tiêm của tôi khi phụ thuộc được tiêm từ cấp thấp nhất của ứng dụng, đó là giao diện người dùng (trong trường hợp của tôi, dự án web .NET). Không gian lận và sử dụng mã IoC trong FirmProxy để làm mới sự phụ thuộc thích hợp cho tôi. Đó là cơ bản bằng cách sử dụng mô hình Service Locator dù sao đi nữa.

Tôi nghĩ về NHiberante và Enitity Framework, và có vẻ như họ không có vấn đề gì về cách tạo sql cho một ví dụ đơn giản mà tôi đã trình bày.

Có ai khác có bất kỳ ý tưởng/phương pháp/etc nào khác ... sẽ giúp tôi đạt được những gì tôi muốn làm mà không có ORM không?

Hoặc có thể có một cách khác tốt hơn để tiếp cận điều này? Tôi không muốn mất là khả năng truy cập Công ty từ Người dùng hoặc để có danh sách Người dùng cho một Công ty cụ thể

+0

Bình chọn để di chuyển đến http://programmers.stackexchange.com/ – MattDavey

+0

@ indiecodemonkey Bạn đã tìm thấy một giải pháp? –

Trả lời

5

Bạn cần suy nghĩ rõ hơn về các đối tượng gốc tổng hợp của mình, tức là trọng tâm của mỗi kho lưu trữ. Bạn không tạo một kho lưu trữ cho mỗi bảng trong cơ sở dữ liệu của bạn, đó không phải là mục tiêu. Mục đích là để xác định đối tượng gốc tổng hợp mà bạn cần phải làm việc, bao gồm các bảng/bản ghi con, tức là Người dùng (Công ty, Địa chỉ, Quyền Truy cập). Đó là những gì kho lưu trữ của bạn sẽ trở lại ứng dụng của bạn.

Khó khăn bạn đang gặp đang cho bạn thấy rằng các kho lưu trữ của bạn không được cấu trúc đúng. Bất cứ lúc nào tôi tìm thấy mã của tôi trở nên quá khó khăn nó làm tăng một cảnh báo với tôi rằng tôi có thể làm điều đó sai, tôi sống cuộc sống của tôi bằng cách đó;)

+1

+1 - trong trường hợp này có vẻ như cả Công ty và Người dùng đều là tổng hợp gốc. Trong các kịch bản này, tôi thường có một tham chiếu mềm giữa hai gốc (thông qua một khóa tham chiếu), và nếu tôi cần một cấu trúc dữ liệu nối chúng lại với nhau, tôi làm điều đó một cách rõ ràng (có thể là một đối tượng truy vấn hoặc một mô hình khung nhìn vv). – MattDavey

+0

Tôi thấy Công ty là gốc Tổng hợp và Người dùng nằm trong thư mục gốc đó. Nhưng đồng thời, tôi không thấy Công ty là một tổng thể gốc được chứng minh bằng cách tôi có thể muốn làm việc với một Người dùng, và không bận tâm chút nào với Công ty của Người dùng. Tuy nhiên, khi tôi tiếp tục duy trì User trở lại db, trong ví dụ root của agg, tôi phải kiên trì tất cả các Firm, và tất cả các User của nó. Thật thú vị những gì MattDavey nói về họ CẢNH SÁT là nguồn gốc trầm trọng. Anh ta dẫn tôi trở lại những tài liệu tham khảo mềm mại, điều mà tôi ước tôi có thể tránh xa, tôi thích sử dụng các tham chiếu đối tượng từ mỗi thực thể. –

+1

Cách tôi thường nghĩ về nguồn gốc tổng hợp là do quy tắc 'xóa tầng'. Nếu bạn xóa gốc tổng hợp, mọi thứ bên dưới cũng sẽ bị xóa. Nếu bạn xóa một công ty, tất cả người dùng có nên bị xóa không? – MattDavey

Các vấn đề liên quan