2009-05-29 39 views
7

Đây là khái niệm liên quan đến câu hỏi của tôi here. Tuy nhiên, tôi đã chơi đùa với NHibernate, và nhận ra cốt lõi thực sự của câu hỏi của tôi là gì.Thực hiện bất kỳ .NET ORM nào có sử dụng các hàm tạo "đúng" không?

Trong thiết kế OO cổ điển, để đóng gói dữ liệu đúng cách, đó là một mẫu phổ biến để chuyển các giá trị vào hàm tạo của đối tượng được lưu trữ trong các thành viên dữ liệu (trường). Những giá trị đó nên không được thay đổi chỉ được hiển thị với người truy cập (thuộc tính chỉ đọc). Những người được phép thay đổi có cả accessors và mutators (đọc-ghi tài sản). Dường như với tôi rằng thích hợp O/RM nên tôn trọng các quy ước này và sử dụng các nhà thầu có sẵn khi tạo một đối tượng. Dựa vào các thuộc tính đọc-ghi, phản chiếu, hoặc các phương thức hackish (IMHO) khác có vẻ ... sai.

Có một giải pháp .NET O/RM ngoài đó thực hiện điều này không?

EDIT

Để giải quyết điểm Praveen, tôi biết rằng có những dự án mà có một 'mặc định' thuật toán cho việc lựa chọn một nhà xây dựng - StructureMap, ví dụ, luôn luôn sử dụng các nhà xây dựng với hầu hết các đối số, trừ bạn đánh dấu một hàm tạo với thuộc tính tùy chỉnh. Tôi có thể thấy đây là một cách hiệu quả để xử lý tình huống. Có lẽ sử dụng một container IoC ngoài ORM sẽ cung cấp loại giải pháp mà tôi cần - mặc dù có vẻ như với tôi rằng đây là, trong khi không vốn xấu, một bước bổ sung không cần thiết để sử dụng ORM.

+0

Có một câu hỏi thú vị khác: Có thể hàm tạo chứa một số logic nghiệp vụ không? I E. khi tôi tạo cá thể của lớp A, hàm tạo của nó có thể tạo cá thể của lớp B liên tục và gán nó cho thuộc tính với trình thiết lập riêng. –

+1

@ Alex: Mục tiêu của tôi là có các đối tượng miền được kiên trì không biết gì. Có logic để tạo ra một đối tượng kiên trì từ đối tượng miền vi phạm điều đó và kết hợp chặt chẽ đối tượng miền với phương thức persistence - một Bad Thing khác. –

Trả lời

1

Bởi Jove, tôi nghĩ rằng tôi đã có nó!

Cảm ơn tất cả mọi người vì đã nhập, nhưng tôi sẽ phải trả lời câu hỏi của riêng tôi. Tôi chỉ mất một giờ để đào bới thông qua PoEAA catalog, suy ngẫm các nguyên tắc OO và trộn lẫn với suy nghĩ sâu sắc về ngôn ngữ C# và khung công tác .NET.

Câu trả lời tôi đưa ra, một cần rằng tôi không thể giải quyết đúng cách bằng cách sử dụng các nhà xây dựng, kết thúc không liên quan đến bản thân nhà xây dựng cuối cùng. Đó là lazy loading! Về cơ bản, mà không triển khai tải chậm trong các lớp miền của bạn (không có sự thiếu hiểu biết và tính linh hoạt), không có cách nào để làm điều đó mà không có phân lớp lớp con. Phân lớp này là lý do NHibernate yêu cầu các thuộc tính ảo.

Tôi vẫn nghĩ rằng việc sử dụng một hàm tạo thay vì phản xạ hoặc một số phương pháp khác để điền vào các trường của lớp cha sẽ tốt hơn (ít nhất là cho các bộ sưu tập ... tải lười không có vị trí của nó), nhưng tôi chắc chắn xem nơi constructor no-arg có vị trí của nó.

+0

Tôi không thực sự hiểu được kết luận bạn đã đến đây ... Có phải bạn nghĩ rằng nó cho phép đối tượng miền có một hàm tạo mặc định, vì nó thể hiện một cái gì đó về miền, viz. rằng các đối tượng có khả năng bị lười tải? Điều đó dường như vẫn liên quan đến sự kiên trì, không phải là logic miền ... – Domenic

+0

Kết luận của tôi là có những vấn đề "cơ học" liên quan khiến cho cách tiếp cận "thuần khiết" trở nên khó khăn, nếu không phải là không thể, để thực hiện. Tôi vẫn không thực sự thích nó, nhưng mặt thực dụng của tôi sẵn sàng đi cùng với nó để hoàn thành công việc. –

2

Thật không may điều này là không thể trong .NET mà không cần bạn đánh dấu các nhà thầu theo một cách nào đó.

Chữ ký phương thức được lưu trữ cho mỗi hàm tạo trong siêu dữ liệu assembly chỉ chứa kiểu của mỗi tham số cho hàm tạo. Không có cách nào cho bất kỳ NET ORM thực sự biết được constructor nào để sử dụng. Tất cả các ORM thấy là một cái gì đó như thế này:

.ctor() 
.ctor(string, string) 
.ctor(string, string, string) 

Không có cách nào cho ORM để biết được .ctor tham số tương ứng với FirstName, LastName và Middlename cho đối tượng khách hàng của bạn ví dụ.

Để cung cấp cho bạn hỗ trợ này, .NET ORM phải hỗ trợ đọc trong thuộc tính tùy chỉnh mà bạn xác định cho từng thông số. Bạn cần đánh dấu các nhà thầu của mình như sau:

Khách hàng công khai ([Thuộc tính ("FirstName")] string FirstName, [Property ("LastName")] string LastName, [Property ("MiddleName")] string MiddleName)

này có 2 nhược điểm:

  1. không có cách nào (mà tôi có thể nghĩ đến, một người nào đó có thể sẽ đúng cho tôi), mà điều này có thể đi vào một tập tin bản đồ.
  2. Bạn vẫn cần phải viết cùng một ánh xạ như bạn đã luôn làm, vì ORM vẫn cần có khả năng nhận các giá trị riêng lẻ cho mỗi thuộc tính.

Vì vậy, bạn cần phải làm tất cả công việc phụ này đánh dấu các nhà thầu và đồng thời, bạn vẫn cần phải ánh xạ các lớp của bạn CHÍNH XÁC như bạn đã làm trước đây.

+1

Tôi không phản đối việc ánh xạ các lớp - tất nhiên, tôi thích nó để đánh dấu mã nguồn. Bạn có nêu ra một số điểm thú vị về những khó khăn vốn có - cảm ơn. –

+1

"Chữ ký phương thức được lưu trữ cho mỗi hàm tạo trong siêu dữ liệu assembly" cũng bao gồm tên tham số, không chỉ kiểu của nó. ORM có thể khớp các thông số với các thuộc tính theo tên mà không yêu cầu bất kỳ ánh xạ nào (quy ước trên cấu hình). Tuy nhiên, vẫn có vấn đề trong đó ctor sử dụng. – Lucas

+0

@Lucas: Rất đúng - Trình phản xạ .NET hiển thị tên của các tham số. Ai biết được - có lẽ tôi đã tìm thấy một ngứa tôi cần phải trầy xước ở đây. –

3

Tôi nghĩ hầu hết các ORM thực sự hỗ trợ khái niệm này, ít nhất là DataObject.Net thực hiện.Mã này hoạt động như nó mong đợi:

[HierarchyRoot(typeof(KeyGenerator), "Id")] 
public class Message : Entity 
{ 
    [Field] 
    public int Id { get; private set; } 

    [Field(Length = 100)] 
    public string Text { get; private set; } 

    public Message(string Text) 
    { 
    Text = text; 
    } 
} 

EDIT: DataObjects lưu trữ dữ liệu trong trạng thái giao dịch nội bộ, và sử dụng constructor thể hóa đặc biệt, được tạo ra bởi PostSharp. Tất nhiên nó không quá tầm thường nếu ORM sử dụng các đối tượng poco.

0

Điều đó phụ thuộc vào những gì bạn xem xét các giá trị không nên thay đổi. Autoincrements, cột tính toán, vv, là ứng cử viên tốt cho việc này.

Chắc chắn có thể, tôi sử dụng ORM mà tôi đã viết và nó ném ngoại lệ nếu bạn cố gắng đặt giá trị của thuộc tính chỉ đọc.

Cập nhật:

Ghi nhà xây dựng được sử dụng cho dữ liệu tiếp tục tồn tại là tốt. Đó là một mô hình phổ biến để đối tượng của bạn chấp nhận (các) PK trong hàm khởi tạo và nó sẽ tự động tìm nạp bản ghi đó.

+0

Nếu nó tạo thuộc tính, nó sẽ không làm cho nó một máy phát điện mã chứ không phải là một O/RM (hoặc là cả hai)? –

+0

Cả hai! Không có quy tắc nào mà ORM phải tự động tạo ánh xạ. – RedFilter

+1

Nếu PK của bạn là một khóa thay thế, sử dụng PK trong một hàm tạo sẽ cho tôi, có một chút mùi mã. Nếu phím * business * là PK, thì điều đó có ý nghĩa hơn một chút, nhưng trong trường hợp đó tôi đề nghị xây dựng đối tượng là cách tiếp cận sai - chuyển PK vào kho sẽ là một tuyến tốt hơn. –

0

Đó là - trong trường hợp chung - không thể cho một OR Mapper chỉ sử dụng các nhà xây dựng. Giả sử một đối tượng được khởi tạo bởi các giá trị được truyền cho hàm tạo và sau đó trạng thái được sửa đổi bằng các cuộc gọi phương thức. Trong trường hợp này, đối tượng có thể được tiếp tục tồn tại với một trạng thái không có trạng thái ban đầu hợp lệ và do đó từ chối bởi hàm tạo.

Vì vậy, có thể bằng trình tạo OR như vậy, nhưng chắc chắn sẽ có giới hạn. Như minh họa bởi ví dụ đã cho, tôi sẽ không xem xét bỏ qua việc đóng gói đối tượng bởi một trình ánh xạ OR làm thiết kế xấu, nhưng như một yêu cầu trong một số trường hợp.

+1

Tôi không thể xem đây là đối số hợp lệ. Nếu trạng thái tồn tại của một đối tượng là hợp lệ cho một hàm khởi tạo, nó phải hợp lệ cho bất kỳ điểm nào trong vòng đời của một đối tượng. Bất kỳ đối tượng nào có trạng thái hợp lệ của nó thay đổi trong suốt cuộc đời của nó có thể là một cái gì đó bạn không tồn tại (hoặc chỉ tồn tại một phần), hoặc có lẽ cần tái cấu trúc. –

+0

Đối số đi theo hướng khác - một trạng thái hợp lệ trong suốt thời gian cuộc sống không nhất thiết phải hợp lệ trong thời gian xây dựng. Lấy ví dụ như một hệ thống quản lý vòng đời cho những gì có thể, có thể là các dự án. Chúng luôn được tạo với trạng thái ban đầu và sau đó trạng thái thay đổi trong khi dự án tiếp tục cho đến khi bạn đạt đến trạng thái đã hoàn thành, đã đóng hoặc lưu trữ. Bạn thậm chí không cần một consructor mà đưa nhà nước như là một đối số bởi vì nhà nước luôn luôn là ngầm trạng thái ban đầu. Nhưng tất nhiên bạn phải có khả năng tồn tại và truy xuất dự án ở bất kỳ trạng thái nào. –

+1

Tôi nghĩ rằng khẳng định của bạn là sai - Tôi thấy không có lý do tại sao một đối tượng dự án không thể được khởi tạo trong trạng thái mở, đã hoàn thành, được lưu trữ hoặc bất kỳ trạng thái * hợp lệ khác * nào. Nếu một dự án không thể được lưu trữ cho đến khi kết thúc, sau đó có một trạng thái lưu trữ với một ngày kết thúc chưa xảy ra là không hợp lệ, nhưng có một dự án mới được khởi tạo ở trạng thái lưu trữ không nhất thiết phải như vậy. Phương thức chọn phương thức khởi tạo 'tham lam' sẽ thực sự hỗ trợ phương thức này (một ctor less- hoặc no-arg sẽ không được chọn theo mặc định, nhưng có sẵn nếu nó có ý nghĩa để có nó). –

1

Tại sao bạn nghĩ điều này sai? Bạn có muốn OR/M của bạn thực hiện logic nghiệp vụ khi tái tạo một đối tượng không? IMHO, không.

Khi bạn tải một đối tượng từ DB, thì OR/M sẽ có thể hoàn nguyên đối tượng, bất kể là gì. Thiết lập một số giá trị trong khi reconsituting, không nên dẫn đến kích hoạt một số loại logic mà thay đổi một giá trị khác (mà có lẽ nên được đưa ra một giá trị của ORM cũng ...)

Mặc dù vậy, tôi nghĩ rằng một nhà xây dựng nên chỉ chứa các tham số cho các trường đó là bắt buộc tại thời điểm tạo đối tượng để có đối tượng trong trạng thái 'hợp lệ'. Bên cạnh đó, bạn có thể có các thuộc tính chỉ đọc công khai đã được cung cấp một giá trị bằng một số phép tính và cũng cần phải được duy trì.
Nếu bạn cảm thấy sự phản chiếu là một 'mùi' để hoàn nguyên các đối tượng, bạn sẽ đối phó với tình huống này như thế nào? Bạn sẽ phải bằng cách nào đó tạo ra một phương pháp công cộng mà sẽ có thể thiết lập giá trị 'chỉ đọc', nhưng điều này phá vỡ đóng gói, và bạn không wan't rằng một trong hai.

+1

Loại tài sản chỉ đọc công khai nào được tạo ra bởi phép tính, sẽ tồn tại mà không phải là một phần của trạng thái tồn tại của đối tượng? Dữ liệu mà phép tính đó sẽ hoạt động như thế nào? Nếu đó là dữ liệu từ đối tượng, thì bạn có thể dễ dàng tái tạo nó. Nếu đó là dữ liệu bên ngoài đối tượng, tôi cho rằng dữ liệu hoặc trường chỉ đọc ở sai vị trí. –

+0

@Harper: "Nếu đó là dữ liệu từ đối tượng, thì bạn có thể dễ dàng khôi phục lại", trong một thuộc tính chỉ đọc thuận tiện! nó tương tự như một cột được tính toán trong một DB – Lucas

+0

Hoặc, những gì về somekind của 'tình trạng' cờ mà bạn có trong đối tượng của bạn. Giả sử bạn có một cờ 'PropertyChanged' mà bạn đặt bất cứ khi nào người dùng thay đổi thuộc tính, nhưng bạn không muốn đặt cờ này khi đối tượng được tải từ DB. –

0

Ông Skeet đề xuất mẫu 'thợ xây' trước đây. Tôi đã biến nó thành lớp công khai trong lớp POCO. Nó có các thuộc tính giống như POCO, nhưng tất cả đều đọc/ghi. POCO là chỉ đọc khi cần thiết, nhưng SET RIÊNG. Bằng cách đó, người xây dựng có thể thiết lập sự thích hợp khi POCO được khởi tạo và không có args sôi nổi cho một nhà xây dựng. Loại 'popcicle immutability' ở đó.

+0

Điều này dường như vi phạm nguyên tắc DRY - tôi phản đối các nhiệm vụ lặp đi lặp lại, đặc biệt là khi tôi nghĩ rằng chúng không thực sự là phương pháp phù hợp. –

+0

Tôi hiểu. Những lỗi của tôi về cách tiếp cận constructor-parameters là nó vẫn còn là một nơi khác cho tôi để liệt kê các thích hợp. Các tham số constructor liệt kê chúng; constructor setting private fields liệt kê chúng; và các cánh đồng ... Phải thay đổi một parmeter? brrrr – n8wrl

+0

Vâng ... ngay cả phương pháp C++ (danh sách khởi tạo) không phải là * nhiều * tốt hơn. Tuy nhiên, phương pháp tiếp cận tham số hàm tạo gần hơn để phù hợp với các nguyên tắc OO mà tôi đã được dạy waaaaaay trở lại trong ngày (OK, 10 năm trước - tôi không phải là cũ!). –

1

Tôi đồng ý với một trong các áp phích trước đó mà các nhà thầu nên tạo để tạo một đối tượng khi bắt đầu cuộc đời. Sử dụng một hàm tạo để hydrate một đối tượng hiện có đang ở trạng thái lưu trữ là phản trực giác tốt nhất.

Những gì cần được xem xét và tất cả các ORM chính bị thiếu tại thời điểm này, là tái cấu thành đối tượng miền từ trạng thái đã biết cuối cùng của nó, như được lưu trữ trong cơ sở dữ liệu hoặc bất kỳ kho dữ liệu nào khác vốn là một hoạt động xây dựng hoặc sửa đổi; do đó, không nên sử dụng các hàm tạo hoặc các bộ định vị thuộc tính. Thay vào đó, cơ cấu khuôn khổ tương ứng chặt chẽ nhất với loại hoạt động này là serialization! Đã có các mẫu đã được công nhận để lưu trữ trạng thái của đối tượng và sau đó tái tạo nó thông qua giao diện ISerializable, constructor serialization chuẩn, vv. Có một đối tượng cho cơ sở dữ liệu về cơ bản không khác với sự tồn tại của nó đối với một luồng; trên thực tế, một trong các giá trị của liệt kê StreamingContextStates được sử dụng trong quá trình tuần tự hóa là Persistence !. IMHO, đây sẽ là cách tiếp cận tiêu chuẩn khi thiết kế bất kỳ cơ chế kiên trì nào. Thật không may, tôi không biết của bất kỳ ORM hỗ trợ này ra khỏi hộp.

Cũng cần lưu ý rằng một đối tượng đã được thiết kế để tuần tự hóa vẫn là POCO; sự thiếu hiểu biết dai dẳng đã KHÔNG bị vi phạm. Điểm mấu chốt ở đây là một đối tượng miền nên biết rõ nhất dữ liệu nào cần thiết để tồn tại và khôi phục nó, cũng như thứ tự dữ liệu nào cần được khôi phục (thường là vấn đề). Điều mà đối tượng KHÔNG cần phải biết là cơ chế cụ thể mà nó sẽ được lưu trữ: cơ sở dữ liệu quan hệ, tệp phẳng, blob nhị phân, v.v.

+1

Dài trước khi tồn tại ISerializable, nó đã được phổ biến (và không phản trực giác!) Để sử dụng các constructor để deserialize các đối tượng. Một hàm tạo tạo ra một cá thể của một đối tượng - không có liên kết cố hữu với lời gọi hàm tạo và tuổi thọ của thực thể * miền *. Đối tượng, vâng ... nhưng đối tượng là một * đại diện * của thực thể miền, không phải là thực thể. –

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