2010-04-02 34 views
9

Tôi đã thiết kế phần truy cập dữ liệu trong khung của mình để mỗi khi đối tượng kinh doanh (BO) cần tương tác với cơ sở dữ liệu, nó sẽ phải mở kết nối, gọi truy cập dữ liệu lớp (để thực hiện truy vấn), và sau đó đóng kết nối. Sau đó, nếu nó cần để chạy trong một giao dịch, nó sẽ mở kết nối, bắt đầu giao dịch, gọi lớp truy cập dữ liệu (để thực hiện truy vấn) và sau đó cam kết giao dịch, đóng giao dịch và cuối cùng đóng kết nối.Mở và đóng kết nối cơ sở dữ liệu bên trong giao dịch

Tôi đã làm theo cách này trong suy nghĩ của "mở muộn, đóng sớm" ... nhưng nếu tôi cần gọi các BO khác để gửi dữ liệu trong một giao dịch thì sao? Có cách nào tốt hơn để xử lý các kết nối mở và đóng cũng như làm việc với các giao dịch không?

Tôi là tân binh trong thiết kế kiến ​​trúc ứng dụng, vì vậy tôi hy vọng tôi không làm điều này sai ... bất kỳ trợ giúp nào được đánh giá cao.

Trả lời

5

Nếu một đối tượng kinh doanh cho nhu cầu để thực hiện phương pháp khác nhau trong một giao dịch, sử dụng một TransactionScope như vậy:

using (var transactionScope = new TransactionScope()) 
{ 
    this.Save(); 
    childObjA.Save(); 
    childObjB.Save(); 
    childObjC.Save(); 
    childObjD.Save(); 

    transactionScope.Complete(); 
} 

Nếu bất kỳ của các đối tượng ném một ngoại lệ, nó sẽ rollback giao dịch.

Xem the MSDN reference page for TransactionScope để biết thêm.

+3

Mặc dù 'TransactionScope' * là * lớp để sử dụng ở đây, với thiết kế hiện tại của OP nó sẽ dẫn đến một giao dịch phân tán hoặc có thể là ngoại lệ nếu MSDTC bị tắt hoặc bị khóa. Đó là * có lẽ * không phải là kết quả mong muốn. – Aaronaught

+1

@Aaronaught, với SQL Server 2008 và .NET 3.5 giao dịch sẽ không được quảng bá cho một giao dịch phân tán (đối với thiết kế hiện tại). –

+2

@Tuzo: Đúng, với SQL Server 2008, bạn có thể gian lận vấn đề đa kết nối ** NẾU ** tất cả các kết nối đến cùng một cơ sở dữ liệu ** và ** chỉ có một lần mở cùng một lúc. Nó vẫn là một thực tế khá đáng ngờ, đặc biệt là khi thiết kế thay thế được dễ dàng hơn để xây dựng/duy trì anyway. – Aaronaught

2

Có vẻ như bạn đã có ý tưởng đúng. Nếu nhiều BO cần tham gia, thì một trong số chúng cần phải là "bộ điều khiển" —, nó sẽ mở và đóng kết nối và truyền cho người khác. Hoặc một số đối tượng "wrapper" có thể xử lý kết nối và chuyển nó cho từng BO. BO của bạn có thể cần được thiết kế để hoạt động cả hai (xử lý kết nối của riêng họ) và chấp nhận kết nối hiện có từ bên ngoài.

4

Khi trừu tượng bậc cao phụ thuộc vào trừu tượng mức thấp hơn (chẳng hạn như lớp logic nghiệp vụ tùy thuộc vào kết nối dữ liệu), việc cung cấp trừu tượng mức thấp hơn thông qua hàm tạo là phổ biến. Kỹ thuật này được gọi là constructor tiêm:

public class OrderService 
{ 
    private SqlConnection connection; 

    public OrderService(SqlConnection connection) 
    { 
     if (connection == null) 
      throw new ArgumentNullException("connection"); 
     this.connection = connection; 
    } 

    // Other methods 
} 

này sau đó cho phép bạn viết mã chống lại các dịch vụ tương tự như sau:

using (TransactionScope tsc = new TransactionScope()) 
using (SqlConnection connection = new SqlConnection(...)) 
{ 
    connection.Open(); 
    OrderService os = new OrderService(connection); 
    os.ProcessOrder(myOrder); 
    ShippingService ss = new ShippingService(connection); 
    ss.ShipOrder(myOrder); 
    tsc.Complete(); 
} 

Mà rất có thể sẽ trở thành những gì bạn muốn, trong cuối cùng - khả năng chia sẻ một kết nối giữa nhiều dịch vụ.

Điều này cũng giúp decouple dịch vụ của bạn từ các chi tiết triển khai của kết nối dữ liệu. Bằng cách đó, nếu bạn muốn làm điều gì đó như thay đổi cài đặt kết nối trong một số trường hợp nhất định, bạn không phải tìm hiểu chi tiết về 50 dịch vụ khác nhau, bạn chỉ phải thay đổi một dòng mã tạo kết nối.

Một điều nữa: Nếu bạn định sử dụng TransactionScope, hãy đảm bảo thêm Transaction Binding=Explicit Unbind vào chuỗi kết nối, nếu không thực sự có thể kết thúc với dữ liệu không nhất quán nếu một giao dịch hết giờ.

+1

Theo msdn, "Bắt đầu trong .NET Framework phiên bản 4, những thay đổi đối với Implicit Unbind làm cho Clear Unbind lỗi thời." http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.100).aspx Câu trả lời hay và ví dụ! – xkingpin

2

Như đã đề cập bởi những người khác, TransactionScope là cách để thực hiện.

Nếu bạn đang sử dụng SQL Server 2008 và .NET 3.5, tôi sẽ sửa đổi thiết kế để có đối tượng kinh doanh kiểm soát giao dịch và để mở và đóng kết nối với lớp dữ liệu.

Với tính năng kết nối kết nối, bạn sẽ không thực sự phải gánh chịu chi phí mở kết nối cơ sở dữ liệu vật lý và kết nối của bạn sẽ chỉ mở khi thực hiện công việc thực tế. Kể từ khi (tôi giả định) bạn có SQL Server 2008 with .NET 3.5 your transaction will not escalate to a distributed transaction (trừ khi bạn mở nhiều kết nối cùng một lúc) để bạn tận dụng tối đa cả hai thế giới.

Sau đó, bạn có thể viết đối tượng doanh nghiệp của bạn như thế này:

using (TransactionScope transactionScope = new TransactionScope()) 
{ 
    DataObject dataObject = new DataObject(); 
    dataObject.UpdateQuantity(...); 

    ShippingManager shippingManager = new ShippingManager(); 
    shippingManager.ShipOrder(...); 

    transactionScope.Complete() 
} 

này tránh được việc phải vượt qua chuỗi kết nối xung quanh để tất cả các đối tượng kinh doanh và làm việc điều phối các giao dịch dễ dàng.

Cập nhật

Vẻ đẹp của System.Transactions là tất cả các giao dịch được quản lý cho bạn không phụ thuộc vào kết nối mà bạn đang sử dụng. Bạn chỉ cần khai báo một TransactionScope và tất cả các truy cập cơ sở dữ liệu trong TransactionScope đó sẽ xảy ra với một giao dịch duy nhất (trừ khi bạn yêu cầu khác với các thiết lập TransactionScope khác).

Trong quá khứ (SQL Server 2005 .NET 2.0), nếu bạn đã mở và đóng kết nối rồi mở và đóng kết nối khác (ngay cả với cùng chuỗi kết nối) thì giao dịch được đẩy từ Giao dịch nhẹ đến Phân phối Giao dịch. Điều này là không mong muốn bởi vì hiệu suất bị (thông tin liên lạc với MSDTC là quá trình và giao thức cam kết hai pha) và MSDTC có thể là một nỗi đau để cấu hình trong nhiều môi trường sản xuất (tường lửa và bảo mật).

Với SQL Server 2008 và .NET 3.5, họ đã thêm khả năng tránh quảng cáo này khi mở và đóng nhiều kết nối với cùng một chuỗi kết nối trong một giao dịch. Để có giải thích thực sự tốt về những gì họ đã thấy, hãy xem Extending Lightweight Transactions in SqlClient.

Cập nhật 2

Giao dịch với Oracle 10g sẽ hoạt động đúng với TransactionScope. Và có vẻ như ODP.NET supports Lightweight Transactions (điều này rất hay). Thật không may, tôi nghĩ rằng việc quảng cáo cho một giao dịch phân phối sẽ xảy ra với việc đóng và mở các kết nối.

Nếu bạn muốn tránh một giao dịch phân tán, bạn có thể chuyển kết nối đến mọi cuộc gọi phương thức/Đối tượng kinh doanh. Nếu bạn không muốn truyền kết nối, bạn có thể sử dụng ConnectionScope class để giữ kết nối mở trên chuỗi. Một thay thế cho điều đó sẽ là sử dụng Enterprise Library 3.0 (và cao hơn) Khối ứng dụng truy cập dữ liệu. Data Access Block can detect that a transaction is in progress and use the same connection để tránh giao dịch được phân phối.

+0

Nếu bạn có kết nối mở và đóng xử lý lớp dữ liệu, bạn sử dụng giao dịch như thế nào? Nếu bạn có ba BO cần phải làm điều gì đó và tất cả họ đều trên cùng một giao dịch, thì tất cả họ đều không phải sử dụng cùng một kết nối? –

+0

Tôi đã suy nghĩ về việc sử dụng khối Truy cập dữ liệu trong Thư viện doanh nghiệp. Tôi không chắc tôi có đủ thời gian để viết lại phần đó của đơn đăng ký hay không. Tôi sẽ phải sử dụng EL hoặc vượt qua kết nối xung quanh mỗi BO của tôi. BO và các thực thể của tôi là một trong những điều tương tự, vì vậy tôi có thể cần chia nhỏ chúng ra để làm cho công việc này tốt hơn. –

1

Có thể bạn đang tìm kiếm mẫu Unit of Work và mẫu Registry. Hai mẫu này có thể hoạt động trong buổi hòa nhạc để tách biệt các mối quan tâm về việc tìm kiếm các đối tượng nghiệp vụ và theo dõi chúng sau này để cam kết lưu trữ dữ liệu của bạn dưới dạng giao dịch.

Tôi cũng sẽ xem xét Ánh xạ quan hệ đối tượng hoặc ORM. ORM là một thành phần mức độ cao hơn của đơn vị công việc, đăng ký, sự thiếu hiểu biết và các mẫu khác cung cấp một sự tách biệt rất rõ ràng về logic nghiệp vụ của bạn từ logic bền bỉ của bạn. Sử dụng và ORM, bạn thường có thể loại bỏ sự cần thiết phải viết các thủ tục lưu sẵn, xây dựng một DAL tùy chỉnh, vv ORM chăm sóc các mối quan tâm liên tục của bạn cho bạn, cho phép bạn tập trung vào logic nghiệp vụ cần được thực hiện.

Vì bạn đang sử dụng C# và .NET, tôi sẽ xem xét Khuôn khổ thực thể (v4, không sử dụng v1) hoặc LINQ to SQL. Cả hai đều là OR mappers đi kèm với .NET framework từ v3.5 trở đi. LINQ to SQL là một ORM rất đơn giản và có nhiều công cụ giúp bạn đi rất nhanh. Entity Framework là một ORM phong phú hơn nhiều, cũng được sử dụng rất tốt (tốt hơn LINQ to SQL), và cung cấp nhiều chức năng hơn đáng kể. Ngoài ra còn có ORM của bên thứ ba có thể thực hiện công việc, bao gồm một công cụ miễn phí có tên NHibernate. Trong khi nó không phải là công cụ tốt như của Microsoft ORM, NHibernate là một ORM mã nguồn mở rất trưởng thành với một cộng đồng lớn sau đây.

Nếu một ORM không phải là một khả năng, sau đó tôi sẽ nhìn vào Unit of Work, Registry (hoặc Repository), Persistence Vô Minh, Separation of Concerns, Single Responsibility, và các mẫu khác có liên quan.

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