2017-05-24 13 views
5

मेरे .NET कोर एप्लिकेशन में, मेरे पास एक सजावटी वर्ग है जो मुझे आशा है कि लेनदेनस्कोप में डेटाबेस कमांड के निष्पादन को लपेटकर लेनदेन को संभालने में सक्षम होगा। दुर्भाग्यवश, ऐसा लगता है कि ट्रांज़ेक्शनस्कोप के लिए समर्थन इसे .NET कोर 2: https://github.com/dotnet/corefx/issues/19708:ट्रांज़ेक्शनस्कोप के बिना नेट कोर ट्रांजैक्शन सजावटी

ट्रांज़ेक्शनस्कोप की अनुपस्थिति में, मुझे इस समस्या का सबसे अच्छा तरीका नहीं है । TransactionScope के साथ, मेरे लेनदेन डेकोरेटर इस तरह दिखता है:

public class TransactionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> decorated; 

    //constructor   

    public void Handle(TCommand command) 
    { 
     using (var scope = new TransactionScope()) 
     { 
      this.decorated.Handle(command); 

      scope.Complete(); 
     } 
    } 
} 

वर्तमान में, ICommandHandler में से प्रत्येक के कार्यान्वयन एक उदाहरण मेरे DapperContext वर्ग के इस तरह हो जाता है और आदेशों संभालता है:

public void Handle(UpdateEntity command) 
    { 
     var sql = Resources.UpdateEntityPart1; 

     this.context.Execute(sql, new 
     { 
      id = command.Id;    
     }); 

     var sql = Resources.UpdateEntityPart2; 

     //call Execute again 
    } 

DapperContext वर्ग एक कनेक्शन कारखाना है प्रत्येक निष्पादन विधि के लिए प्रत्येक कॉल के लिए नए कनेक्शन प्रदान करने के लिए। चूंकि कमांड हैंडलर को एक एकल टीकॉमैंड के लिए एकाधिक डेटाबेस लिखना पड़ सकता है, इसलिए कुछ विफल होने पर मुझे रोलबैक करने की क्षमता की आवश्यकता होती है। लेनदेन बनाने के साथ-साथ जब मैं कनेक्शन बनाता हूं (डैपरकॉन्टेक्स्ट में) का मतलब है कि मेरे पास कनेक्शन में लेनदेन संबंधी व्यवहार की गारंटी देने का कोई तरीका नहीं है।

एक विकल्प मैं माना जाता है नहीं लगता है कि सभी संतोषजनक:

  1. आदेश हैंडलर स्तर पर कनेक्शन और लेनदेन प्रबंधित करें, तो व्यवसायिक संदर्भ के लिए है कि जानकारी गुजरती हैं। इस तरह दिए गए आदेश के लिए सभी प्रश्न एक ही कनेक्शन और लेनदेन का उपयोग करते हैं। यह काम कर सकता है, लेकिन मुझे इस जिम्मेदारी के साथ अपने कमांड हैंडलरों को बोझ करने का विचार पसंद नहीं है। समग्र डिजाइन के संदर्भ में, यह अधिक स्वाभाविक लगता है कि डैपरकॉन्टेक्स्ट कनेक्शन होने के बारे में चिंता करने का स्थान है।

मेरा प्रश्न, फिर: क्या ट्रांज़ेक्शनस्कोप के उपयोग के बिना लेनदेन सजावट लिखने का कोई तरीका है, एनईटी कोर में एसक्लोनकनेक्शन की वर्तमान सीमाओं को देखते हुए? यदि नहीं, तो अगला सबसे अच्छा समाधान क्या है जो एकल जिम्मेदारी के सिद्धांत का भी उल्लंघन नहीं करता है?

+2

यदि मैं आप थे, तो मैं माइक्रोसॉफ्ट को समझाने के लिए उस विशेष गिरहूब मुद्दे पर टिप्पणी करूंगा कि यह एक बड़ी असंगतता है और बिल्कुल ठीक किया जाना चाहिए। मुझे लगता है कि ट्रांजैक्शनस्कोप कोर 2 में काम नहीं करता है तो कई संगठनों के लिए यह एक बड़ी समस्या है। – Steven

उत्तर

4

एक समाधान डेकोरेटर के हिस्से के रूप में एक SqlTransaction बनाते हैं, और ThreadLocal या AsyncLocal क्षेत्र के कुछ प्रकार में संग्रहीत करना हो सकता है, तो यह व्यापार लेनदेन के अन्य भागों के लिए उपलब्ध है, भले ही यह स्पष्ट रूप से पारित नहीं है । यह प्रभावी रूप से TransactionScope कवर के तहत करता है (लेकिन अधिक सुंदरता से)।

public class TransactionCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> decorated; 
    private readonly AsyncLocal<SqlTransaction> transaction; 

    public void Handle(TCommand command) 
    { 
     transaction.Value = BeginTranscation(); 

     try 
     { 
      this.decorated.Handle(command); 

      transaction.Value.Commit(); 
     } 
     finally 
     { 
      transaction.Value.Dispose(); 
      transaction.Value = null; 
     } 
    } 
} 

एक अमूर्त इसके साथ ही संचालकों का उपयोग कर सकते हैं::

:

public interface ITransactionContainer 
{ 
    SqlTransaction CurrentTransaction { get; } 
} 


public void Handle(UpdateEntity command) 
{ 
    // Get current transaction 
    var transaction = this.transactionContainer.CurrentTransaction; 

    var sql = Resources.UpdateEntityPart1; 

    // Pass the transaction on to the Execute 
    // (or hide it inside the execute would be even better) 
    this.context.Execute(sql, transaction, new 
    { 
     id = command.Id;    
    }); 

    var sql = Resources.UpdateEntityPart2; 

    //call Execute again 
} 

ITransactionContainer के लिए एक कार्यान्वयन कुछ इस तरह दिख सकता है

उदाहरण के रूप में, इस छद्म कोड पर एक नज़र

public class AsyncTransactionContainer : ITransactionContainer 
{ 
    private readonly AsyncLocal<SqlTransaction> transaction; 

    public AsyncTransactionContainer(AsyncLocal<SqlTransaction> transaction) 
    { 
     this.transaction = transaction; 
    } 

    public SqlTransaction CurrentTransaction => 
     this.transaction.Value 
      ?? throw new InvalidOperationException("No transaction."); 
} 

AsyncTransactionContainer औरदोनोंAsyncLocal<SqlTransaction> पर निर्भर करता है। यह एक सिंगलटन होना चाहिए (उसी उदाहरण को दोनों में इंजेक्शन दिया जाना चाहिए)।

+0

सलाह की सराहना करते हैं, स्टीवन। मैं फिलहाल इसका परीक्षण करने में सक्षम नहीं हूं, लेकिन मुझे आश्चर्य है कि क्या मुझे निष्पादन के लिए कई कॉलों में एक ही SqlConnection इंस्टेंस को रखने की आवश्यकता होगी। चूंकि SqlTransaction किसी विशिष्ट कनेक्शन से जुड़ा हुआ है, इसलिए मुझे यकीन नहीं है कि क्या होगा यदि मैं डैपर को CurrentTransaction के साथ कॉल करने का प्रयास करता हूं लेकिन नए बनाए गए कनेक्शन से। – Matt

+0

इसका मतलब है एक ही SqlConnection का पुन: उपयोग करना। कनेक्शन को सजावटी के अंदर भी खोला/बंद करने की जरूरत है। – Steven

+0

यदि आप करेंगे तो दो और प्रश्न: क्या कोई कारण है कि लेनदेन कंटेनर में एक सेटटर नहीं जोड़ना ताकि सजावटी एक इंटरफ़ेस पर भरोसा कर सके? सोचें कि यूनिट परीक्षण आसान बना देगा .. इसके अलावा, इस मामले में सिंगलटन आवश्यक है? AsyncLocal फ़ंक्शन के उदाहरण के लिए कई उदाहरण नहीं हैं (उदाहरण के लिए AsyncScoped लाइफस्टाइल का उपयोग करना), उदाहरण के बाद से केवल ExecutionContext से पढ़ते हैं और लिखते हैं, जिन्हें उन्हें बुलाया जाता है? बीटीडब्ल्यू, इसे पढ़ने वाले किसी और के लिए, मैं जिस कमांडरलर/सजावटी संरचना का उपयोग कर रहा हूं वह स्टीवन की वेबसाइट से आया था। – Matt

संबंधित मुद्दे