2015-03-26 9 views
7

में सार तत्वों की क्वेरी करना मैं टेबल प्रति पदानुक्रम डेटाबेस विरासत का उपयोग कर रहा हूं जहां सभी व्युत्पन्न प्रकारों के लिए कॉलम एक ही तालिका में हैं। प्रत्येक व्युत्पन्न तालिका एक स्ट्रिंग Discriminator क्षेत्र है कि व्युत्पन्न वर्ग के नाम रखती है का उपयोग कर की पहचान की है: उम्मीदडैपर

--------------------- 
| tanimal   | 
--------------------- 
| animalid   | 
| discriminator  | 
| furcolour   | 
| feathercolour  | 
--------------------- 

public abstract class Animal 
{ 
    public int AnimalId { get; set; } 
    public string Discriminator { get { return GetType().Name; } } 
} 

public class Bird : Animal 
{ 
    public string FeatherColour { get; set; } 
} 

public class Dog : Animal 
{ 
    public string FurColour { get; set; } 
} 

के रूप में, जब डैप्पर की क्वेरी प्रणाली द्वारा इस पुन: प्राप्त करने मैं Instances of abstract classes cannot be created प्राप्त करते हैं। मैं आशा करता हूं कि यह जानवरों की सूची को उनके मूल्यों के साथ संबंधित व्युत्पन्न प्रकारों के साथ वापस कर देगा।

var animals = Connection.Query<Animal>("SELECT * FROM tanimal") 

इसके लिए समर्थन जोड़ने के मेरे प्रयास असफल रहे हैं। एक अमूर्त वर्ग तो है में से पहले SqlMapper.cs :: GetTypeDeserializer() यदि प्रकार पारित किया जा रहा कहा जाता है मैं एक के साथ प्रकार निम्न विधि में लौट आए बदल देते हैं:

static Type GetDerivedType(Type abstractType, IDataReader reader) 
{ 
    var discriminator = abstractType.GetProperty("Discriminator"); 
    if (discriminator == null) 
     throw new InvalidOperationException("Cannot create instance of abstract class " + abstractType.FullName + ". To allow dapper to map to a derived type, add a Discriminator field that stores the name of the derived type"); 

    return Type.GetType((string)reader["Discriminator"]); 
} 

हालांकि यह इस बिंदु पर की तरह दिखता है पाठक खोला नहीं गया है इसलिए यह Invalid attempt to read when no data is present के साथ विफल रहता है।

क्या यह सही दृष्टिकोण है? क्या इस जगह का समर्थन करने के लिए कोई प्रयास किया गया है?

+0

https: // GitHub। कॉम/स्टैक एक्सचेंज/डैपर-डॉट-नेट/अंक/262 – ajbeaven

उत्तर

3

आप यह काम कर सकते हैं लेकिन यह अलग टेबल के साथ डैपर के डिफ़ॉल्ट व्यवहार का उपयोग करने से कम कुशल होगा।

GetDeserializer जरूरतों हर पंक्ति है, जो यह while (reader.Read())

अंदर होने QueryImpl<T> आप परिणाम आप चाहते हैं प्राप्त कर सकते हैं संशोधित करके की जरूरत का मतलब है के लिए कहा जाता है। मान लें कि आप के साथ परिणाम हो रही है:

var results = connection.Query<Animal>("SELECT * FROM tanimal"); 

फिर QueryImpl<T> की try {} ब्लॉक की शुरुआत हो जाएगा:

try 
{ 
cmd = command.SetupCommand(cnn, info.ParamReader); 

if (wasClosed) cnn.Open(); 

// We can't use SequentialAccess any more - this will have a performance hit. 
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default); 
wasClosed = false; 

// You'll need to make sure your typePrefix is correct to your type's namespace 
var assembly = Assembly.GetExecutingAssembly(); 
var typePrefix = assembly.GetName().Name + "."; 

while (reader.Read()) 
{ 
    // This was already here 
    if (reader.FieldCount == 0) //https://code.google.com/p/dapper-dot-net/issues/detail?id=57 
     yield break; 

    // This has been moved from outside the while 
    int hash = GetColumnHash(reader); 

    // Now we're creating a new DeserializerState for every row we read 
    // This can be made more efficient by caching and re-using for matching types 
    var discriminator = reader["discriminator"].ToString(); 
    var convertToType = assembly.GetType(typePrefix + discriminator); 

    var tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(convertToType, reader, 0, -1, false)); 
    if (command.AddToCache) SetQueryCache(identity, info); 

    // The rest is the same as before except using our type in ChangeType 
    var func = tuple.Func; 

    object val = func(reader); 
    if (val == null || val is T) 
    { 
     yield return (T)val; 
    } 
    else 
    { 
     yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture); 
    } 
} 
// The rest of this method is the same 

यह केवल discriminator क्षेत्र के साथ विधि काम कर देगा, तो आप कर सकते हैं यदि आपको अन्य प्रश्नों के साथ सामान्य रूप से काम करने की आवश्यकता है तो अपना खुद का QueryImpl<T> बनाना चाहते हैं। इसके अलावा मैं गारंटी नहीं दे सकता कि यह हर मामले में काम करेगा, केवल दो पंक्तियों के साथ परीक्षण किया जाएगा, प्रत्येक प्रकार में से एक - लेकिन यह एक अच्छा प्रारंभिक बिंदु होना चाहिए।

+0

बहुत बढ़िया! इसके लिए धन्यवाद :) भले ही मैं अलग-अलग तालिकाओं के साथ टीपीटी विरासत का उपयोग कर रहा था, फिर भी आपको अमूर्त प्रकारों से पूछने में समस्या होगी। मैंने जिथब पर एक मुद्दा बनाया है यदि आप इसे वहां भी पोस्ट करना चाहते हैं: https://github.com/StackExchange/dapper-dot-net/issues/262 – ajbeaven

+0

हां आपको अभी भी सार प्रकारों में कोई समस्या होगी, और यदि आपके पास एक छोटा डेटासेट है तो उपरोक्त कोड के साथ प्रदर्शन प्रभावित नगण्य हो सकता है। – embee

2

मैं अपना समाधान भी साझा करना चाहता हूं। इनपुट:

सी #

abstract class Stock {} 
class Bond: Stock {} 
class Equity : Stock {} 

एसक्यूएल

CREATE TABLE [dbo].[Stocks] (
....some columns.... 
    [Descriminator] VARCHAR (100) NOT NULL, 
); 

एसक्यूएल में मैं एक Descriminator स्तंभ जो प्रत्येक पंक्ति "इक्विटी" या "बांड" के लिए सी # प्रकार निर्धारित करता है है। मूल रूप से, यदि टेबल-प्रति-पदानुक्रम रणनीति है तो यह एक मानक कार्यान्वयन है।

मैं डैप्पर के paremeter कम क्वेरी सिंटैक्स

connection.Query(sql); 

इस्तेमाल किया एक dynamic वस्तु जो साफ-सुथरी DapperRow रूप में देखता है पाने के लिए। हालांकि DapperRow एक निजी वर्ग है, यह IDictionary<string, object>. स्ट्रिंग - एक संपत्ति का नाम, ऑब्जेक्ट - गुण मान लागू करता है।

समारोह Convert IDictionary<string, object> to class (दृढ़ता से टाइप किया):

public static T GetObject<T>(IDictionary<string, object> dict) 
{ 
    Type type = typeof(T); 
    var obj = Activator.CreateInstance(type); 

    foreach (var kv in dict) 
    { 
     type.GetProperty(kv.Key).SetValue(obj, kv.Value); 
    } 
    return (T)obj; 
} 

और मैपर descriminator स्तंभ और सी # वर्ग के बीच:

public static Stock ConvertToStock(object value) 
{ 
    var dapperRowProperties = value as IDictionary<string, object>; 
    switch (dapperRowProperties["Descriminator"]) 
    { 
     case "Bond": 
      return GetObject<Bond>(dapperRowProperties); 
     case "Stock": 
      return GetObject<Stock>(dapperRowProperties); 
     default: 
      return null; 
    } 
} 

कनवर्टर का उपयोग:

public Stock GetStock(int id) 
{ 
    Stock stock; 
    var sql = "select * from Stocks where Id = @id"; 
    using (var connection = ConnectionFactory.GetOpenConnection()) 
    { 
     stock = connection.Query(sql, new { id }).Select(ConvertToStock).Single(); 
    } 
    return stock; 
} 
+1

+1 इसके लिए बहुत अच्छा धन्यवाद। संभवतः यह एक बड़े डेटासेट से निपटने के दौरान काफी अक्षम होगा लेकिन यह सब कुछ ठीक है। – ajbeaven

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