डापर में मल्टीपैपिंग का सही उपयोग


111

मैं ProductItems और संबंधित ग्राहकों की सूची वापस करने के लिए डैपर के मल्टीपैपिंग फीचर का उपयोग करने की कोशिश कर रहा हूं।

[Table("Product")]
public class ProductItem
{
    public decimal ProductID { get; set; }        
    public string ProductName { get; set; }
    public string AccountOpened { get; set; }
    public Customer Customer { get; set; }
} 

public class Customer
{
    public decimal CustomerId { get; set; }
    public string CustomerName { get; set; }
}

मेरा डैपर कोड इस प्रकार है

var sql = @"select * from Product p 
            inner join Customer c on p.CustomerId = c.CustomerId 
            order by p.ProductName";

var data = con.Query<ProductItem, Customer, ProductItem>(
    sql,
    (productItem, customer) => {
        productItem.Customer = customer;
        return productItem;
    },
    splitOn: "CustomerId,CustomerName"
);

यह ठीक काम करता है लेकिन मुझे लगता है कि सभी ग्राहकों की संपत्तियों को वापस करने के लिए स्प्लिटऑन पैरामीटर में पूरी कॉलम सूची को जोड़ना होगा। अगर मैं "CustomerName" नहीं जोड़ता, तो यह अशक्त हो जाता है। क्या मैं मल्टीमैपिंग फीचर की मुख्य कार्यक्षमता को समझने से चूक गया हूं। मैं हर बार कॉलम नामों की एक पूरी सूची नहीं जोड़ना चाहता।


कैसे आप वास्तव में datagridview में दोनों तालिकाओं को दिखाने के लिए करते हैं? एक छोटे से उदाहरण की बहुत सराहना की जाएगी।
अंकुर सोनी

जवाबों:


184

मैंने सिर्फ एक परीक्षण चलाया जो ठीक काम करता है:

var sql = "select cast(1 as decimal) ProductId, 'a' ProductName, 'x' AccountOpened, cast(1 as decimal) CustomerId, 'name' CustomerName";

var item = connection.Query<ProductItem, Customer, ProductItem>(sql,
    (p, c) => { p.Customer = c; return p; }, splitOn: "CustomerId").First();

item.Customer.CustomerId.IsEqualTo(1);

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

अपने रिकॉर्ड को इस तरह देखें:

ProductID | उत्पाद नाम | खाता बंद | CustomerId | ग्राहक का नाम
--------------------------------------- ----------- --------------

डैपर को इस क्रम में कॉलम को 2 ऑब्जेक्ट में विभाजित करने का तरीका जानने की जरूरत है। सरसरी नज़र से पता चलता है कि ग्राहक स्तंभ पर शुरू होता है CustomerId, इसलिए splitOn: CustomerId

यहाँ एक बड़ा चेतावनी है, यदि अंतर्निहित तालिका में स्तंभ आदेश किसी कारण से फ़्लिप किया गया है:

ProductID | उत्पाद नाम | खाता बंद | ग्राहक नाम | ग्राहक आईडी, ग्राहक पहचान  
--------------------------------------- ----------- --------------

splitOn: CustomerId एक अशक्त ग्राहक नाम में परिणाम होगा।

यदि आप CustomerId,CustomerNameविभाजन बिंदुओं के रूप में निर्दिष्ट करते हैं , तो डैपर मान लेता है कि आप परिणाम को 3 वस्तुओं में विभाजित करने की कोशिश कर रहे हैं। पहला शुरू में शुरू होता है, दूसरा शुरू होता है CustomerId, तीसरा शुरू होता है CustomerName


2
धन्यवाद सैम। हाँ, आपका अधिकार स्तंभों का वापसी क्रम था जो कि CustomerName के साथ समस्या थी CustomerId लौटाया जा रहा है CustomerName वापस शून्य आ रहा था।
रिचर्ड फॉरेस्ट

18
याद रखने वाली एक बात यह है कि आपके पास रिक्त स्थान नहीं हो सकते spliton, अर्थात CustomerId,CustomerNameनहीं CustomerId, CustomerName, क्योंकि डैपर Trimस्ट्रिंग विभाजन के परिणाम नहीं देता है। यह सिर्फ जेनेरिक स्प्लिटन एरर को फेंक देगा। मुझे एक दिन पागल कर दिया।
जेस

2
@vaheeds में आपको हमेशा कॉलम के नामों का उपयोग करना चाहिए और कभी भी स्टार का उपयोग नहीं करना चाहिए, यह करने के लिए sql को कम काम देता है, और आपको ऐसी परिस्थितियां नहीं मिलती हैं जहां कॉलम क्रम गलत है, जैसा कि इस मामले में है।
हरगै

3
@vaheeds - आईडी, आईडी, आईडी के बारे में जो डैपर कोड देख रहा है वह संवेदनशील नहीं है, और यह स्प्लिटऑन के लिए टेक्स्ट को भी ट्रिम करता है - यह डैपर का v1.50.2.0 है।
हरगै

2
किसी को भी आश्चर्य होता है, यदि आपको 3 ऑब्जेक्ट में एक क्वेरी को विभाजित करना है: "Id" नाम के एक कॉलम पर और "somethingId" नाम के एक कॉलम पर, विभाजन खंड में पहले "Id" को शामिल करना सुनिश्चित करें। भले ही डैपर "आईडी" पर डिफ़ॉल्ट रूप से विभाजित करता है, इस मामले में इसे स्पष्ट रूप से सेट किया जाना है।
एसबीयू

27

हमारी तालिकाओं का नाम आपके समान है, जहां "ग्राहक" जैसी चीज को 'चयन *' ऑपरेशन का उपयोग करके दो बार लौटाया जा सकता है। इसलिए, डैपर अपना काम कर रहा है, लेकिन बहुत जल्दी (संभवतः) विभाजित हो रहा है, क्योंकि कॉलम होंगे:

(select * might return):
ProductID,
ProductName,
CustomerID, --first CustomerID
AccountOpened,
CustomerID, --second CustomerID,
CustomerName.

यह स्पिरिटोन बनाता है: पैरामीटर इतना उपयोगी नहीं है, खासकर जब आप यह सुनिश्चित नहीं करते हैं कि कॉलम किस क्रम में लौटाए गए हैं। बेशक आप कॉलम को मैन्युअल रूप से निर्दिष्ट कर सकते हैं ... लेकिन यह 2017 है और हम शायद ही कभी ऐसा करते हैं कि अब मूल वस्तु मिलती है।

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

select 
p.*,

c.CustomerID AS Id,
c.*

... देखा! Dapper केवल डिफ़ॉल्ट रूप से Id पर विभाजित होगा, और वह Id सभी ग्राहक कॉलम से पहले होता है। बेशक यह आपके रिटर्न रिजल्ट में एक अतिरिक्त कॉलम जोड़ देगा, लेकिन यह जानने की अतिरिक्त उपयोगिता के लिए बेहद न्यूनतम ओवरहेड है कि कौन सा कॉलम किस वस्तु से संबंधित है। और आप आसानी से इसका विस्तार कर सकते हैं। पते और देश की जानकारी चाहिए?

select
p.*,

c.CustomerID AS Id,
c.*,

address.AddressID AS Id,
address.*,

country.CountryID AS Id,
country.*

सभी के सर्वश्रेष्ठ, आप स्पष्ट रूप से दिखा रहे हैं कि किस वस्तु के साथ कौन से कॉलम जुड़े हुए हैं। बाकी काम डापर करता है।


यह एक संक्षिप्त दृष्टिकोण है जब तक कि कोई तालिका में Id फ़ील्ड नहीं है।
बर्नार्ड वेंडर बीकेन

इस दृष्टिकोण के साथ एक तालिका में अभी भी एक आईडी फ़ील्ड हो सकती है ... लेकिन यह PK होना चाहिए। आपको सिर्फ उपनाम नहीं बनाना होगा, इसलिए यह वास्तव में थोड़ा कम काम है। (मुझे लगता है कि यह बहुत ही असामान्य (खराब रूप होगा?) 'Id' नामक एक कॉलम होगा जो PK नहीं है।)
ब्लेक

5

निम्नलिखित संरचना को मानते हुए जहां '|' बंटवारे की बात है और Ts ऐसी इकाइयाँ हैं जिन पर मानचित्रण लागू किया जाना चाहिए।

       TFirst         TSecond         TThird           TFourth
------------------+-------------+-------------------+------------
col_1 col_2 col_3 | col_n col_m | col_A col_B col_C | col_9 col_8
------------------+-------------+-------------------+------------

निम्नलिखित डैपर क्वेरी है जिसे आपको लिखना होगा।

Query<TFirst, TSecond, TThird, TFourth, TResut> (
    sql : query,
    map: Func<TFirst, TSecond, TThird, TFourth, TResut> func,
    parma: optional,
    splitOn: "col_3, col_n, col_A, col_9")

इसलिए हम TFirst के लिए col_1 col_2 col_3 को मैप करना चाहते हैं, TSecond के लिए col_n col_m ...

स्प्लिटऑन एक्सप्रेशन का अनुवाद इस प्रकार है:

TF स्तंभ में सभी स्तंभों की मैपिंग तब तक शुरू करें जब तक कि आप 'col_3' के रूप में नाम या अलियास किए गए कॉलम को न खोज लें, और मैपिंग परिणाम में 'col_3' को भी शामिल करें।

फिर TSecond में 'col_n' से शुरू होने वाले सभी कॉलमों की मैपिंग शुरू करें और नए विभाजक मिलने तक मैपिंग जारी रखें, जो इस स्थिति में 'col_A' है और TThird मैपिंग की शुरुआत को चिह्नित करता है और इसलिए एक।

Sql क्वेरी के कॉलम और मैपिंग ऑब्जेक्ट के प्रॉप्स 1: 1 रिलेशन में हैं (जिसका अर्थ है कि उन्हें समान नाम दिया जाना चाहिए), यदि sql क्वेरी से उत्पन्न कॉलम के नाम अलग हैं, तो आप उन्हें 'AS' का उपयोग करके उपनाम दे सकते हैं। कुछ_आलियास_नाम] की अभिव्यक्ति।


2

एक और कैविएट है। अगर CustomerId फ़ील्ड शून्य है (आमतौर पर बाईं ओर के प्रश्नों में) तो Dapper ProductItem को ग्राहक = null के साथ बनाता है। ऊपर के उदाहरण में:

var sql = "select cast(1 as decimal) ProductId, 'a' ProductName, 'x' AccountOpened, cast(null as decimal) CustomerId, 'n' CustomerName";
var item = connection.Query<ProductItem, Customer, ProductItem>(sql, (p, c) => { p.Customer = c; return p; }, splitOn: "CustomerId").First();
Debug.Assert(item.Customer == null); 

और यहां तक ​​कि एक और चेतावनी / जाल। यदि आप स्प्लिटऑन में निर्दिष्ट फ़ील्ड को मैप नहीं करते हैं और उस फ़ील्ड में नल डैपर बनाता है और संबंधित ऑब्जेक्ट को भरता है (ग्राहक इस मामले में)। पिछले वर्ग के साथ इस वर्ग का उपयोग करने के लिए:

public class Customer
{
    //public decimal CustomerId { get; set; }
    public string CustomerName { get; set; }
}
...
Debug.Assert(item.Customer != null);
Debug.Assert(item.Customer.CustomerName == "n");  

क्या ग्राहक को वर्ग में जोड़ने के अलावा दूसरे उदाहरण का कोई हल है? मुझे एक मुद्दा मिल रहा है जहां मुझे एक अशक्त वस्तु की आवश्यकता है, लेकिन यह मुझे एक खाली वस्तु दे रहा है। ( stackoverflow.com/questions/27231637/… )
jmzagorski

1

मैं अपने रेपो में यह उदारतापूर्वक करता हूं, मेरे उपयोग के मामले के लिए अच्छा काम करता है। मुझे लगा कि मैं शेयर करूंगा। शायद कोई इसे आगे बढ़ाएगा।

कुछ कमियां हैं:

  • यह मानता है कि आपके विदेशी प्रमुख गुण आपके बच्चे की वस्तु + "Id" का नाम हैं, जैसे UnitId।
  • मेरे पास माता-पिता के लिए केवल 1 बच्चे की वस्तु है।

कोड:

    public IEnumerable<TParent> GetParentChild<TParent, TChild>()
    {
        var sql = string.Format(@"select * from {0} p 
        inner join {1} c on p.{1}Id = c.Id", 
        typeof(TParent).Name, typeof(TChild).Name);

        Debug.WriteLine(sql);

        var data = _con.Query<TParent, TChild, TParent>(
            sql,
            (p, c) =>
            {
                p.GetType().GetProperty(typeof (TChild).Name).SetValue(p, c);
                return p;
            },
            splitOn: typeof(TChild).Name + "Id");

        return data;
    }

0

यदि आपको एक बड़ी इकाई को लिखने की आवश्यकता है, तो प्रत्येक क्षेत्र को एक कठिन कार्य होना चाहिए।

मैंने @BlackjacketMack उत्तर देने की कोशिश की, लेकिन मेरी एक तालिका में एक अन्य कॉलम है (मुझे नहीं पता कि यह डीबी डिज़ाइन की समस्या है, लेकिन ...) तो यह डपर पर एक अतिरिक्त विभाजन सम्मिलित करता है, इसीलिए

select
p.*,

c.CustomerID AS Id,
c.*,

address.AddressID AS Id,
address.*,

country.CountryID AS Id,
country.*

मेरे लिए काम नहीं करता है। फिर मैंने इसे थोड़ा बदलाव के साथ समाप्त कर दिया, बस एक विभाजन बिंदु को एक नाम के साथ सम्मिलित करें जो तालिकाओं पर किसी भी क्षेत्र के साथ मेल नहीं खाता as Idहै as _SplitPoint_, द्वारा मामला बदल सकता है , अंतिम sql स्क्रिप्ट इस तरह दिखता है:

select
p.*,

c.CustomerID AS _SplitPoint_,
c.*,

address.AddressID AS _SplitPoint_,
address.*,

country.CountryID AS _SplitPoint_,
country.*

फिर डैपर में इस के रूप में सिर्फ एक स्प्लिटऑन जोड़ें

cmd =
    "SELECT Materials.*, " +
    "   Product.ItemtId as _SplitPoint_," +
    "   Product.*, " +
    "   MeasureUnit.IntIdUM as _SplitPoint_, " +
    "   MeasureUnit.* " +
    "FROM   Materials INNER JOIN " +
    "   Product ON Materials.ItemtId = Product.ItemtId INNER JOIN " +
    "   MeasureUnit ON Materials.IntIdUM = MeasureUnit.IntIdUM " +
List < Materials> fTecnica3 = (await dpCx.QueryAsync<Materials>(
        cmd,
        new[] { typeof(Materials), typeof(Product), typeof(MeasureUnit) },
        (objects) =>
        {
            Materials mat = (Materials)objects[0];
            mat.Product = (Product)objects[1];
            mat.MeasureUnit = (MeasureUnit)objects[2];
            return mat;
        },
        splitOn: "_SplitPoint_"
    )).ToList();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.