2010-01-21 23 views
6

Tôi có một phương pháp mà tôi muốn "giao dịch" theo nghĩa trừu tượng. Nó gọi hai phương thức xảy ra để làm việc với cơ sở dữ liệu, nhưng phương pháp này không biết điều đó.Phương pháp biết quá nhiều về các phương pháp gọi là

public void DoOperation() 
{ 
    using (var tx = new TransactionScope()) 
    { 
     Method1(); 
     Method2(); 

     tc.Complete(); 
    } 
} 

public void Method1() 
{ 
    using (var connection = new DbConnectionScope()) 
    { 
     // Write some data here 
    } 
} 

public void Method2() 
{ 
    using (var connection = new DbConnectionScope()) 
    { 
     // Update some data here 
    } 
} 

Bởi vì trên thực tế các TransactionScope có nghĩa là một giao dịch cơ sở dữ liệu sẽ được sử dụng, chúng ta có một vấn đề mà nó cũng có thể được đề bạt lên một giao dịch phân tán, nếu chúng ta có được hai kết nối khác nhau từ hồ bơi.

tôi có thể sửa lỗi này bằng gói phương pháp DoOperation() trong một ConnectionScope:

public void DoOperation() 
{ 
    using (var tx = new TransactionScope()) 
    using (var connection = new DbConnectionScope()) 
    { 
     Method1(); 
     Method2(); 

     tc.Complete(); 
    } 
} 

tôi đã DbConnectionScope bản thân mình vì chỉ một mục đích như vậy, vì vậy mà tôi không cần phải vượt qua đối tượng kết nối đến tiểu phương (đây là ví dụ nhiều hơn so với vấn đề thực sự của tôi). Tôi đã có ý tưởng từ bài viết này: http://msdn.microsoft.com/en-us/magazine/cc300805.aspx

Tuy nhiên tôi không thích cách giải quyết này vì nó có nghĩa DoOperation hiện nay có kiến ​​thức mà các phương pháp đó đang gọi có thể sử dụng một kết nối (và từng có thể là một kết nối khác nhau). Làm thế nào tôi có thể cấu trúc lại điều này để giải quyết vấn đề?

Một ý tưởng tôi đang nghĩ đến là tạo ra một tổng quát hơn OperationScope, để khi hợp tác với lối sống Castle Windsor tùy chỉnh tôi sẽ viết, sẽ có nghĩa là bất kỳ thành phần nào được yêu cầu của container với OperationScopeLifetyle ví dụ của thành phần đó. Điều này giải quyết được vấn đề vì OperationScope rõ ràng hơn DbConnectionScope.

+0

Cái gì? Ai đó đã bỏ phiếu cho câu hỏi này sẽ bị đóng? Tại sao? –

Trả lời

1

Tôi thấy các yêu cầu xung đột tại đây.

Một mặt, bạn không muốn DoOperation có bất kỳ nhận thức nào về thực tế rằng kết nối cơ sở dữ liệu đang được sử dụng cho các hoạt động phụ của nó.

Mặt khác, rõ ràng là nhận thức được thực tế này vì nó sử dụng số TransactionScope.

tôi có thể loại hiểu những gì bạn đang nhận xét khi bạn nói rằng bạn muốn nó được giao dịch trong nghĩa trừu tượng, nhưng quan điểm của tôi về vấn đề này là nó hầu như không thể (không, đầu rằng - hoàn toàn không thể) để mô tả một giao dịch theo các thuật ngữ trừu tượng như vậy. Hãy chỉ nói rằng bạn có một lớp học như thế này:

class ConvolutedBusinessLogic 
{ 
    public void Splork(MyWidget widget) 
    { 
     if (widget.Validate()) 
     { 
      widgetRepository.Save(widget); 
      widget.LastSaved = DateTime.Now; 
      OnSaved(new WidgetSavedEventArgs(widget)); 
     } 
     else 
     { 
      Log.Error("Could not save MyWidget due to a validation error."); 
      SendEmailAlert(new WidgetValidationAlert(widget)); 
     } 
    } 
} 

Lớp này được thực hiện ít nhất hai điều mà có lẽ không thể được cuộn lại (thiết lập thuộc tính của một lớp và thực hiện các phương thức thụ lý sự kiện, mà có thể cho ví dụ cascade-update một số điều khiển trên một biểu mẫu), và ít nhất hai điều nữa mà chắc chắn không thể được khôi phục (gắn thêm vào tệp nhật ký ở đâu đó và gửi cảnh báo email).

Có lẽ điều này có vẻ giống như một ví dụ giả tạo, nhưng đó thực sự là quan điểm của tôi; bạn không thể xử lý TransactionScope làm "hộp đen".Phạm vi thực tế là sự phụ thuộc giống như bất kỳ loại nào khác; TransactionScope chỉ cung cấp một sự trừu tượng thuận tiện cho một đơn vị công việc có thể không phải lúc nào cũng phù hợp vì nó không thực sự quấn kết nối cơ sở dữ liệu và không thể dự đoán tương lai. Đặc biệt, nó thường không thích hợp khi một hoạt động lôgic đơn lẻ cần kéo dài hơn một kết nối cơ sở dữ liệu, cho dù các kết nối đó có cùng cơ sở dữ liệu hay các kết nối khác nhau hay không. Nó cố gắng xử lý trường hợp này tất nhiên, nhưng như bạn đã học được, kết quả là phụ tối ưu.

Con đường tôi nhìn thấy nó, bạn có một vài tùy chọn khác nhau:

  1. Hãy rõ ràng thực tế là Method1Method2 yêu cầu kết nối bằng cách lấy một tham số kết nối, hoặc bằng cách sắp xếp chúng thành một lớp có sự phụ thuộc kết nối (constructor hoặc property). Bằng cách này, kết nối trở thành một phần của hợp đồng , vì vậy Method1 không còn biết quá nhiều - nó biết chính xác những gì nó được cho là biết theo thiết kế.

  2. Chấp nhận rằng phương pháp DoOperation bạn không có một nhận thức về những gì Method1Method2 làm. Trong thực tế, không có gì sai với điều này! Đúng là bạn không muốn dựa vào chi tiết triển khai của một số cuộc gọi trong tương lai, nhưng phụ thuộc chuyển tiếp trong trừu tượng thường được coi là OK; đó là đảo ngược các phụ thuộc mà bạn cần phải quan tâm, như khi một số lớp sâu trong mô hình miền cố gắng cập nhật điều khiển giao diện người dùng mà doanh nghiệp không biết ngay từ đầu.

  3. Sử dụng mẫu Unit of Work mạnh mẽ hơn (cũng: here). Điều này đang trở nên phổ biến hơn và nó là, theo và lớn, hướng mà Microsoft đã đi vào với LINQ to SQL và EF (DataContext/ObjectContext về cơ bản là triển khai UOW). Tay áo này phù hợp với khung DI và về cơ bản giúp bạn giảm bớt sự lo lắng khi giao dịch bắt đầu và kết thúc và cách truy cập dữ liệu phải xảy ra (thuật ngữ là "sự thiếu hiểu biết"). Điều này có lẽ sẽ yêu cầu làm lại đáng kể thiết kế của bạn, nhưng pound cho pound nó sẽ là dễ nhất để duy trì lâu dài.

Hy vọng một trong số đó sẽ giúp bạn.

+0

Tôi nghĩ rằng điều "phụ thuộc về phía trước" là những gì tạo ra hoặc phá vỡ nó. UoW chắc chắn như thế nào tôi thường tiếp cận này, nhưng tôi đang làm việc trong một ứng dụng di sản có sử dụng datatables (không phải ngay cả datasets) vì vậy nó khó khăn. Cảm ơn câu trả lời, tôi cảm thấy thoải mái hơn một chút với nó trên cơ sở phần thứ hai của câu trả lời của bạn. –

0

Bạn có thể đẩy giao dịch/ghi danh vào DB và xóa hoàn toàn khỏi mã không?

+0

Không thực sự. Trong trường hợp này, hai lệnh DB hoàn toàn riêng biệt được ban hành, nhưng trong trường hợp này tôi muốn cả hai hoặc không phải trong số chúng cam kết nếu một hoặc khác thất bại, hoặc nếu một cái gì đó khác ném một ngoại lệ. –

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