31

में लेनदेन मैं भंडार पैटर्न का उपयोग कर एक लेनदेन तरीके से एक से अधिक इकाई की बचत कैसे कर सकता हूं? उदाहरण के लिए, अगर मैं ऑर्डर जोड़ना चाहता हूं और ऑर्डर सृजन के आधार पर ग्राहक की स्थिति अपडेट करना चाहता हूं, लेकिन ऑर्डर सफलतापूर्वक पूरा होने पर ही ऐसा ही होता है? ध्यान रखें कि इस उदाहरण के लिए, आदेश ग्राहक के अंदर संग्रह नहीं हैं। वे अपनी खुद की इकाई हैं।रिपोजिटरी पैटर्न

यह सिर्फ एक संक्षिप्त उदाहरण है, इसलिए मुझे वास्तव में परवाह नहीं है कि ऑर्डर ग्राहक ऑब्जेक्ट के अंदर या यहां तक ​​कि एक ही बाध्य संदर्भ में होना चाहिए या नहीं। मुझे सच में परवाह नहीं है कि अंतर्निहित तकनीक का उपयोग किया जाएगा (एन हाइबरनेट, ईएफ, एडीओ.Net, लिंक, इत्यादि) मैं सिर्फ यह देखना चाहता हूं कि कुछ कॉलिंग कोड किसी भी या कुछ भी ऑपरेशन के इस स्वीकृत रूप से विकसित उदाहरण में कैसा दिख सकता है।

उत्तर

7

मैं कुछ प्रकार के लेनदेन स्कोप/संदर्भ प्रणाली का उपयोग करने पर विचार करता हूं। तो आपके पास निम्न कोड हो सकता है जो मोटे तौर पर .NET & C# पर आधारित है।

public class OrderService 
{ 

public void CreateNewOrder(Order order, Customer customer) 
{ 
    //Set up our transactional boundary. 
    using (TransactionScope ts=new TransactionScope()) 
    { 
    IOrderRepository orderRepos=GetOrderRespository(); 
    orderRepos.SaveNew(order); 
    customer.Status=CustomerStatus.OrderPlaced; 

    ICustomerRepository customerRepository=GetCustomerRepository(); 
    customerRepository.Save(customer) 
    ts.Commit(); 
    } 
} 
} 

TransactionScope घोंसला तो मान लीजिए कि आप एक कार्रवाई है जो आपके आवेदन एक TransactionScope पैदा करेगा और साथ ही कई सेवाओं को पार कर गया था कर सकते हैं। अब यदि आप ट्रांज़ेक्शनस्कोप का उपयोग करते हैं तो वर्तमान .NET में उन्हें एक डीटीसी के लिए जोखिम उठाने का जोखिम है लेकिन यह भविष्य में हल हो जाएगा।

हमने अपनी खुद की लेनदेनस्कोप कक्षा बनाई है जो मूल रूप से हमारे डीबी कनेक्शन प्रबंधित करता है और स्थानीय एसक्यूएल लेनदेन का उपयोग करता है।

+0

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

+1

कोड में कुछ इस व्यवसाय नियम को संभालना है, इसे इस स्तर पर या उच्च स्तर पर एक बिंदु एक लेनदेनस्कोप के भीतर परिवर्तन कर रहा था या तो स्थानीय लेनदेन या लेनदेन को संभालने के लिए वितरित लेनदेन की अनुमति देता है।यदि व्यवसाय नियम कहता है कि जब भी ऑर्डर दिया जाता है तो ग्राहक को अपडेट करें, तो यह ऑर्डर करने के लिए एक अच्छी जगह है क्योंकि सभी ऑर्डर यहां से गुजरते हैं। – JoshBerke

3

आप कार्य पैटर्न की इकाई को कार्यान्वित करना चाहते हैं। NHibernate के लिए वहाँ कार्यान्वयन हैं। एक राइनो कॉमन्स प्रोजेक्ट में है, मशीन भी है। यूओडब्ल्यू।

5

Spring.NET AOP + NHibernate आप का उपयोग करना सामान्य तौर पर अपने भंडार वर्ग लिख सकते हैं और कस्टम एक्सएमएल फ़ाइल में अपने लेनदेन कॉन्फ़िगर कर सकते हैं:

public class CustomerService : ICustomerService 
{ 
    private readonly ICustomerRepository _customerRepository; 
    private readonly IOrderRepository _orderRepository; 

    public CustomerService(
     ICustomerRepository customerRepository, 
     IOrderRepository orderRepository) 
    { 
     _customerRepository = customerRepository; 
     _orderRepository = orderRepository; 
    } 

    public int CreateOrder(Order o, Customer c) 
    { 
     // Do something with _customerRepository and _orderRepository 
    } 
} 

एक्सएमएल फ़ाइल आपके द्वारा चुने गए जो तरीकों आप के अंदर निष्पादित करने के लिए चाहते हैं में किसी लेन-देन:

<object id="TxProxyConfigurationTemplate" 
      abstract="true" 
      type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> 

    <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/> 

    <property name="TransactionAttributes"> 
     <name-values> 
     <add key="Create*" value="PROPAGATION_REQUIRED"/> 
     </name-values> 
    </property> 
    </object> 

    <object id="customerService" parent="TxProxyConfigurationTemplate"> 
    <property name="Target"> 
     <object type="MyNamespace.CustomerService, HibernateTest"> 
      <constructor-arg name="customerRepository" ref="customerRepository" /> 
      <constructor-arg name="orderRepository" ref="orderRepository" /> 
     </object> 
    </property> 

    </object> 

और अपने कोड में आप इस तरह customerservice वर्ग का एक उदाहरण प्राप्त:

ICustomerService customerService = (ICustomerService)ContextRegistry 
    .GetContent() 
    .GetObject("customerService"); 

स्प्रिंग.NET आपको ग्राहक सेवा कक्षा का प्रॉक्सी वापस कर देगा जो आप CreateOrder विधि को कॉल करते समय लेनदेन लागू करेंगे। इस तरह आपकी सेवा कक्षाओं के अंदर कोई लेनदेन विशिष्ट कोड नहीं है। एओपी इसका ख्याल रखता है। अधिक जानकारी के लिए आप Spring.NET के दस्तावेज़ों पर एक नज़र डाल सकते हैं।

2

मैं एक व्यवहार ढंग भंडार पैटर्न का उपयोग करने में एक इकाई की तुलना में अधिक की बचत कैसे संपुटित करते हैं? उदाहरण के लिए, यदि मैं ऑर्डर सृजन के आधार पर ग्राहक स्थिति को पर आधारित करना चाहता हूं, तो आदेश सफलतापूर्वक पूरा हुआ है, तो केवल क्या है? में रखें कि इस उदाहरण के लिए, ग्राहक ग्राहक के अंदर संग्रह नहीं हैं। वे अपनी खुद की इकाई हैं।

यह भंडार की ज़िम्मेदारी नहीं है, आमतौर पर यह उच्च स्तर पर किया जाता है।यद्यपि आपने कहा कि आपकी विशिष्ट तकनीकों में दिलचस्पी नहीं है, मुझे लगता है कि समाधानों को टालने के लायक है, उदाहरण के लिए जब एक वेब ऐप के साथ एनएचबर्ननेट का उपयोग करते हैं तो आप शायद session-per request का उपयोग करने पर विचार करेंगे।

तो अगर आप एक उच्च स्तर पर लेन-देन का प्रबंधन कर सकते तो मेरे पास दो विकल्प होगा:

  1. अग्रिम जांच - एक सेवा में उदाहरण के लिए व्यवहार में समन्वय करता है, तो आप पूछ कर आगे बढ़ना चाहते हैं तय आदेश/ग्राहक, यदि या तो कहते हैं कि वे नहीं करते हैं तो उनमें से किसी एक को अपडेट करने का भी प्रयास न करें।
  2. रोलबैक - बस ग्राहक/आदेश को अपडेट करना जारी रखें और यदि चीजें डेटाबेस लेनदेन रोलबैक के माध्यम से भाग में विफल हो जाती हैं।

यदि आप दूसरे विकल्प के लिए जाते हैं तो प्रश्न यह है कि इन-मेमोरी ऑब्जेक्ट्स के साथ क्या होता है, आपका ग्राहक असंगत स्थिति में छोड़ा जा सकता है। यदि यह मायने रखता है, और मैं उन परिदृश्यों में काम करता हूं जहां ऐसा नहीं होता है क्योंकि ऑब्जेक्ट केवल उस अनुरोध के लिए लोड किया गया था, तो यदि यह संभव हो तो मैं अग्रिम चेक पर विचार कर रहा हूं क्योंकि यह विकल्पों की तुलना में बहुत आसान है (इसमें वापस रोलिंग - स्मृति परिवर्तन या वस्तुओं को फिर से लोड करना)।

+1

रिपोजिटरी की ज़िम्मेदारी क्यों नहीं है? डोमेन मॉडल से दूर डेटाबेस डेटाबेस संचालन के लिए पूरा विचार नहीं है? मेरे लिए, भंडार उस लेनदेन समर्थन को रखने के लिए सबसे अच्छी जगह है। –

12

आज सुबह मेरे कंप्यूटर को बूट करना मुझे उस परियोजना के लिए सही समस्या का सामना करना पड़ा जिस पर मैं काम कर रहा हूं। मेरे पास कुछ विचार थे जो निम्नलिखित डिज़ाइन का नेतृत्व करते हैं - और टिप्पणियां कमाल से अधिक होंगी। दुर्भाग्यवश जोश द्वारा सुझाए गए डिज़ाइन संभव नहीं हैं, क्योंकि मुझे रिमोट एसक्यूएल सर्वर के साथ काम करना है और यह वितरित लेनदेन समन्वयक सेवा को सक्षम नहीं कर सकता है।

मेरा समाधान मेरे मौजूदा कोड में कुछ सरल परिवर्तनों पर आधारित है।

सबसे पहले, मैं अपने सभी खजाने एक सरल मार्कर इंटरफ़ेस को लागू है:

/// <summary> 
/// Provides methods to enable transaction support. 
/// </summary> 
public interface IHasTransactions : IRepository 
{ 
    /// <summary> 
    /// Initiates a transaction scope. 
    /// </summary> 
    void BeginTransaction(); 

    /// <summary> 
    /// Executes the transaction. 
    /// </summary> 
    void CommitTransaction(); 
} 

विचार में है जो:

/// <summary> 
/// A base interface for all repositories to implement. 
/// </summary> 
public interface IRepository 
{ } 

दूसरे, मैं अपने सभी लेनदेन सक्षम खजाने निम्नलिखित इंटरफ़ेस को लागू करते हैं मेरे सभी रिपॉजिटरीज मैं इस इंटरफ़ेस को कार्यान्वित करता हूं और कोड जोड़ता हूं जो वास्तविक प्रदाता के आधार पर सीधे लेनदेन पेश करता है (नकली भंडारों के लिए मैंने प्रतिनिधियों की एक सूची बनाई है जो प्रतिबद्धता पर निष्पादित हो जाती है)। LINQ SQL करने के लिए यह इस तरह के रूप कार्यान्वयन सुनिश्चित करने के लिए आसान होगा:

#region IHasTransactions Members 

public void BeginTransaction() 
{ 
    _db.Transaction = _db.Connection.BeginTransaction(); 
} 

public void CommitTransaction() 
{ 
    _db.Transaction.Commit(); 
} 

#endregion 
निश्चित रूप से

यह जरूरी है कि एक नया भंडार वर्ग प्रत्येक थ्रेड के लिए बनाई गई है, लेकिन यह मेरा परियोजना के लिए उचित है।

रिपोजिटरी का उपयोग करने वाली प्रत्येक विधि को BeginTransaction() और EndTransaction() का आह्वान करने की आवश्यकता है, यदि भंडार IHasTransactions लागू करता है। इस कॉल को और भी आसान बनाने के लिए, मैं निम्नलिखित एक्सटेंशन के साथ आया:

/// <summary> 
/// Extensions for spawning and subsequently executing a transaction. 
/// </summary> 
public static class TransactionExtensions 
{ 
    /// <summary> 
    /// Begins a transaction if the repository implements <see cref="IHasTransactions"/>. 
    /// </summary> 
    /// <param name="repository"></param> 
    public static void BeginTransaction(this IRepository repository) 
    { 
     var transactionSupport = repository as IHasTransactions; 
     if (transactionSupport != null) 
     { 
      transactionSupport.BeginTransaction(); 
     } 
    } 

    public static void CommitTransaction(this IRepository repository) 
    { 
     var transactionSupport = repository as IHasTransactions; 
     if (transactionSupport != null) 
     { 
      transactionSupport.CommitTransaction(); 
     } 
    } 
} 

टिप्पणियों की सराहना की जाती है!

+0

आप एक संस्करण के साथ भी जा सकते हैं और प्रत्येक लेनदेन के लिए एक भंडार उदाहरण बना सकते हैं, इसे एक प्रयोग कथन के अंदर रख सकते हैं और निपटान() लेनदेन को प्रतिबद्ध कर सकते हैं। कॉलर विधि में लेनदेन के बारे में जानना आवश्यक होगा। –

+0

यह बहुत प्यारा है। –

+0

बहुत दिलचस्प समाधान ... –

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