2013-02-18 8 views
7

मेरे साथियों ने मुझसे पूछा है कि अगर orderline उदाहरण दिया, यह प्रारंभ करने में एक viewmodel इस तरह लग रही संभव होगा:ट्रांस्फोर्मिंग परिणाम

OrderViewModel 
    string OrderId 
    string CustomerName 
    List<OrderLineViewModel> OrderLines 

OrderLineViewModel 
    string ProductName 
    string ROI 
    int Quantity 

एक सूचकांक से?

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

चीयर्स,

जेम्स

संपादित करें:

हम एक प्रश्न से सीधे दृश्य मॉडल पॉपुलेट करने की कोशिश कर रहे हैं। हम निम्नलिखित सूचकांक की कोशिश की:

public class OrdersViewIndex : AbstractIndexCreationTask<Order> 
{ 
    Map = orders => from order in orders 
        select new { 
           OrderId = order.id 
           }; 

    Transform = (database, orders) => from order in orders 
            let customer = database.Load<Customer>(order.customerId) 
            select new { 
                OrderId = order.id, 
                CustomerName = customer.Name, 
                OrderLines = // This is where I struggled to answer my colleagues questions as i'd need to load product name. 
               } 
} 
+0

ज्यादातर मामलों में, आपको एक परिवर्तन की आवश्यकता नहीं है और यह केवल अपने कोड में ही कर सकता है। कृपया जो आपने कोशिश की है उसे पोस्ट करें। –

उत्तर

18

पहले, समझते हैं कि सभी अनुक्रमित स्वचालित रूप से __document_id कहा जाता है एक सूचकांक प्रविष्टि में Id मैप करें। तो इसे फिर से मैप करने में बहुत अधिक मूल्य नहीं है। आप इस इंडेक्स मैप में जो भी कर रहे हैं वह इसे फिर से दूसरी इंडेक्स एंट्री में कॉपी कर रहा है जिसे OrderId कहा जाता है।

दूसरा, समझें कि परिवर्तन वास्तव में सूचकांक का हिस्सा नहीं हैं, लेकिन केवल इंडेक्स परिभाषा से जुड़े हुए हैं और रनटाइम पर निष्पादित हैं। वे जो भी वास्तव में प्रदान करते हैं वह सर्वर पर क्वेरी परिणामों को मॉर्फ करने का एक तरीका है। ज्यादातर मामलों में ये चीजें हैं जो आप क्लाइंट-साइड कर सकते हैं।

तीसरा, इंडेक्स गैर-आईडी फ़ील्ड के खिलाफ पूछताछ के लिए हैं, और possibly stale लेकिन eventually consistent परिणाम प्रदान करते हैं। जब आप अपने Id (दस्तावेज़ कुंजी) द्वारा दस्तावेजों को पुनर्प्राप्त कर रहे हैं, तो किसी भी अनुक्रमणिका का उपयोग करने में कोई बात नहीं है। आप इसके बजाय .Load() विधि का उपयोग करना चाहते हैं, जो ACID गारंटी प्रदान करता है, और केवल डेटाबेस से दस्तावेज़ को पुनर्प्राप्त करता है।

अब - आपके पास ग्राहक का नाम कैसे प्राप्त करें, जब आपके दस्तावेज़ में केवल ग्राहक आईडी है, और केवल उत्पाद आईडी के बजाय उत्पाद का नाम कैसे प्राप्त करें, इस सवाल का सवाल था।

var order = session.Load<Order>(theOrderId); 

लेकिन अब आप इन जैसे कुछ दृश्य मॉडल पॉपुलेट हैं:

public class Order 
{ 
    public string Id { get; set; } 
    public string CustomerId { get; set; } 
    public List<OrderLine> OrderLines { get; set; } 
} 

public class OrderLine 
{ 
    public string ProductId { get; set; } 
    public int Quantity { get; set; } 
} 

public class Customer 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
} 

public class Product 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
} 

आप अपनी आईडी का उपयोग कर एक भी आदेश को पुन: प्राप्त कर रहे हैं, तो आपको निम्न करना होगा: चलो अपने दस्तावेज़ों इस तरह दिखना मान लेते हैं :

public class OrderVM 
{ 
    public string OrderId { get; set; } 
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; } 
    public List<OrderLineVM> OrderLines { get; set; } 
} 

public class OrderLineVM 
{ 
    public string ProductId { get; set; } 
    public string ProductName { get; set; } 
    public int Quantity { get; set; } 
} 

आप Includes का उपयोग करके ऐसा करेंगे।

var order = session.Include<Order>(x => x.CustomerId) 
        .Include<Order>(x => x.OrderLines.Select(y => y.ProductId)) 
        .Load<Order>(theOrderId); 

var orderViewModel = new OrderVM 
{ 
    OrderId = order.Id, 
    CustomerId = order.CustomerId, 
    CustomerName = session.Load<Customer>(order.CustomerId).Name, 
    OrderLines = order.OrderLines.Select(x => new OrderLineVM 
       { 
        ProductId = x.ProductId, 
        ProductName = session.Load<Product>(x.ProductId).Name, 
        Quantity = x.Quantity 
       }) 
}; 

session.Load() को कई कॉल देखकर बावजूद, वहाँ वास्तव में डेटाबेस के लिए केवल एक कॉल है। .Include कथन ने सुनिश्चित किया कि सभी संबंधित दस्तावेज़ पहले कॉल के साथ सत्र में लोड किए गए थे। बाद की कॉल सिर्फ स्थानीय सत्र से बाहर खींचें।

उपर्युक्त सभी अपनी आईडी द्वारा एक ही आदेश पुनर्प्राप्त करने के लिए है।यदि इसके बजाय आप सभी ऑर्डर प्राप्त करना चाहते हैं, या किसी विशेष ग्राहक के लिए सभी ऑर्डर प्राप्त करना चाहते हैं - तो आपको क्वेरी करने की आवश्यकता है।

एक विशेष ग्राहक के आदेश के लिए एक गतिशील क्वेरी इस प्रकार दिखाई देगा:

var results = session.Query<Order>().Where(x => x.CustomerId == theCustomerId); 

आप अपने दृश्य को मॉडल के लिए इन परियोजना के लिए इससे पहले कि आप उपयोग कर सकते हैं की तरह चाहते हैं तो शामिल हैं:

var results = session.Query<Order>() 
    .Customize(x => x.Include<Order>(y => y.CustomerId) 
        .Include<Order>(y => y.OrderLines.Select(z => z.ProductId))) 
    .Where(x => x.CustomerId == theCustomerId) 
    .Select(x => new OrderVM 
    { 
     OrderId = x.Id, 
     CustomerId = x.CustomerId, 
     CustomerName = session.Load<Customer>(x.CustomerId).Name, 
     OrderLines = order.OrderLines.Select(y => new OrderLineVM 
     { 
      ProductId = y.ProductId, 
      ProductName = session.Load<Product>(y.ProductId).Name, 
      Quantity = y.Quantity 
     }) 
    }); 

यह काम करता है, लेकिन हो सकता है कि आप इसे हर बार लिखना न चाहें। साथ ही, पूरे उत्पाद और ग्राहक रिकॉर्ड सत्र में लोड किए जाने चाहिए, जब आप केवल प्रत्येक से एक फ़ील्ड चाहते थे। यह वह जगह है जहां परिवर्तन उपयोगी हो सकते हैं। अब जब आप क्वेरी

public class Orders_Transformed : AbstractIndexCreationTask<Order> 
{ 
    public Orders_Transformed() 
    { 
     Map = orders => from order in orders select new { }; 

     TransformResults = (database, orders) => 
      from order in orders 
      select new 
      { 
       OrderID = order.Id, 
       CustomerID = order.CustomerId, 
       CustomerName = database.Load<Customer>(order.CustomerId).Name, 
       OrderLines = order.OrderLines.Select(y => new 
        { 
         ProductId = y.ProductId, 
         ProductName = database.Load<Product>(y.ProductId).Name, 
         Quantity = y.Quantity 
        }) 
      }; 
    } 
} 

, परिणत पहले से ही आप के लिए डेटा की स्थापना की है: आप इस प्रकार एक स्थिर सूचकांक परिभाषित कर सकते हैं। आपको प्रोजेक्ट में परिणामस्वरूप वीएम निर्दिष्ट करना होगा।

var results = session.Query<Order, Orders_Transformed>().As<OrderVM>(); 

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

मान लें कि अब मैं उन संबंधित क्षेत्रों में से किसी एक से पूछना चाहता हूं। उदाहरण के लिए, मैं जो नाम के ग्राहकों के लिए सभी ऑर्डर प्राप्त करना चाहता हूं। इसे पूरा करने के लिए, मुझे अपने सूचकांक में ग्राहक का नाम शामिल करने की आवश्यकता है। RavenDB 2.0 ने एक सुविधा जोड़ा जो इसे बहुत आसान बनाता है - Indexing Related Documents

आप सूचकांक नक्शा LoadDocument विधि का उपयोग करने को संशोधित करने, इस प्रकार की आवश्यकता होगी:

Map = orders => from order in orders 
       select new 
       { 
        CustomerName = LoadDocument<Customer>(order.CustomerId) 
       }; 

आप चाहें, तो आप या तो शामिल है के साथ इस गठजोड़ कर सकते हैं, या पूर्ण वापस पाने के लिए तकनीक रूपांतरण मॉडल देखें।

एक और तकनीक इन क्षेत्रों को स्टोर करना होगा और project from the index। यह CustomerName जैसे एकल फ़ील्ड के लिए बहुत अच्छी तरह से काम करता है, लेकिन शायद OrderLines जैसे जटिल मानों के लिए अधिक है।

और अंत में, विचार करने के लिए एक और तकनीक denormalization है। एक पल के लिए विचार करें कि Product का नाम बदल सकता है या हटाया जा सकता है। आप शायद पिछले ऑर्डर को अमान्य नहीं करना चाहते हैं। OrderLine ऑब्जेक्ट में आदेश के लिए प्रासंगिक किसी भी उत्पाद डेटा की प्रतिलिपि बनाना एक अच्छा विचार होगा।

public class OrderLine 
{ 
    public string ProductId { get; set; } 
    public string ProductName { get; set; } 
    public decimal Price { get; set; } 
    public int Quantity { get; set; } 
} 

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

public class Orders_ByCustomerName : AbstractIndexCreationTask<Order> 
{ 
    public Orders_ByCustomerName() 
    { 
     Map = orders => from order in orders 
         select new 
         { 
          CustomerName = LoadDocument<Customer>(order.CustomerId).Name 
         }; 

     Store("CustomerName", FieldStorage.Yes); 
    } 
} 

जो तुम साथ आसानी से क्वेरी कर सकते हैं: क्वेरी में

var results = session.Query<OrderVM, Orders_ByCustomerName>() 
        .Where(x => x.CustomerName == "Joe") 
        .As<OrderVM>(); 

ध्यान दें, पहली बार मैं निर्दिष्ट OrderVM, मैं इंडेक्स प्रविष्टियों के आकार को परिभाषित कर रहा हूं। यह सिर्फ लैम्बडा सेट करता है इसलिए मैं x.CustomerName == "Joe" निर्दिष्ट कर सकता हूं।अक्सर, आप इस उद्देश्य के लिए उपयोग की जाने वाली एक विशेष "परिणाम" कक्षा देखेंगे। यह वास्तव में कोई फर्क नहीं पड़ता - मैं किसी भी कक्षा का उपयोग कर सकता था जिसमें CustomerName स्ट्रिंग फ़ील्ड था।

जब मैं .As<OrderVM>() निर्दिष्ट - जो है, जहां मैं वास्तव में एक OrderVM प्रकार के लिए एक Order प्रकार से स्थानांतरित - और के बाद से हम इसके लिए क्षेत्र भंडारण चालू CustomerName क्षेत्र सवारी के लिए साथ आता है।

टी एल; डॉ

RavenDB बहुत सारे विकल्प है। आपकी जरूरतों के लिए क्या काम करता है यह जानने के लिए प्रयोग करें। LoadDocument() के साथ Indexing Related Documents का उचित दस्तावेज़ डिज़ाइन, और सावधानीपूर्वक उपयोग सूचकांक परिवर्तन की आवश्यकता को हमेशा हटा देगा।

+0

मैट बिताए हर समय धन्यवाद, हमेशा के रूप में आपके उत्तर वास्तव में सहायक होते हैं .. यह सब उन पर सबसे ऊपर है! - मैं और काम पर लोग काम करने के लिए संघर्ष कर रहे हैं और कब/whys - मैं सभी को यह जवाब पढ़ने के लिए मिल जाएगा! - एक बार फिर धन्यवाद! – Jamez

+0

रावेनडीबी 2.5 में, अब [परिणाम ट्रांसफॉर्मर्स] (http://ravendb.net/docs/2.5/client-api/querying/results-transformation/result-transformers) की अवधारणा है। मैं जल्द ही उनके उत्तर के लिए इस उत्तर को अपडेट कर दूंगा। –

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