2012-03-29 16 views
23

के साथ किसी संग्रहीत प्रक्रिया के परिणामों को मैप करने के लिए एंटीटी फ्रेमवर्क का उपयोग कैसे करें मैं सी # सर्वर में किसी इकाई को SQL सर्वर संग्रहीत प्रक्रिया के आउटपुट की मैपिंग करने के लिए इकाई फ्रेमवर्क का उपयोग करके एक मूल उदाहरण बनाने का प्रयास कर रहा हूं। , लेकिन अधिक गूढ़ नामों के विपरीत इकाई के पास अलग-अलग (दोस्ताना) नाम पैरामीटर हैं। मैं इसे फ्लुएंट (यानी गैर edmx) वाक्यविन्यास के साथ करने की कोशिश भी कर रहा हूं।अलग-अलग नामित पैरामीटर


क्या काम करता है ....

संग्रहीत प्रक्रिया मान कहा रिटर्न: UT_ID, UT_LONG_NM, UT_STR_AD, UT_CITY_AD, UT_ST_AD, UT_ZIP_CD_AD, UT_CT

अगर मैं इस तरह एक वस्तु बनाने ...

public class DBUnitEntity 
{ 
    public Int16 UT_ID { get; set; } 
    public string UT_LONG_NM { get; set; } 
    public string UT_STR_AD { get; set; } 
    public string UT_CITY_AD { get; set; } 
    public string UT_ST_AD { get; set; } 
    public Int32 UT_ZIP_CD_AD { get; set; } 
    public string UT_CT { get; set; } 
} 

और इस तरह एक EntityTypeConfiguration ...

public class DbUnitMapping: EntityTypeConfiguration<DBUnitEntity> 
{ 
     public DbUnitMapping() 
     { 
      HasKey(t => t.UT_ID); 
     } 
} 

... जो मैं DbContext की OnModelCreating में जोड़ने के लिए, तो मैं संस्थाओं सिर्फ डेटाबेस, जो अच्छा है, इस का उपयोग कर के बाहर ठीक प्राप्त कर सकते हैं ....

var allUnits = _context.Database.SqlQuery<DBUnitEntity>(StoredProcedureHelper.GetAllUnitsProc); 

लेकिन, काम नहीं करता क्या

अगर मैं इस तरह एक इकाई, मित्रवत नाम के साथ चाहते हैं ....

public class UnitEntity : IUnit 
{ 
    public Int16 UnitId { get; set; } 
    public string Name { get; set; } 
    public string Address { get; set; } 
    public string City { get; set; } 
    public string State { get; set; } 
    public Int32 Zip { get; set; } 
    public string Category { get; set; } 
} 

और इस तरह एक EntityTypeConfiguration ...

public UnitMapping() 
    { 
     HasKey(t => t.UnitId); 

     Property(t => t.UnitId).HasColumnName("UT_ID"); 
     Property(t => t.Name).HasColumnName("UT_LONG_NM"); 
     Property(t => t.Address).HasColumnName("UT_STR_AD"); 
     Property(t => t.City).HasColumnName("UT_CITY_AD"); 
     Property(t => t.State).HasColumnName("UT_ST_AD"); 
     Property(t => t.Zip).HasColumnName("UT_ZIP_CD_AD"); 
     Property(t => t.Category).HasColumnName("UT_CT"); 
    } 

जब मैं डेटा मैं संदेश के साथ एक System.Data.EntityCommandExecutionException प्राप्त प्राप्त करने की कोशिश ....

"डेटा रीडर है निर्दिष्ट 'DataAccess.EFCodeFirstSample.UnitEntity' के साथ असंगत। प्रकार के एक सदस्य, 'यूनिट आईडी', के पास समान नाम वाले डेटा रीडर में एक समान कॉलम नहीं है। "

यदि मैं इकाई में" संग्रहीत प्रक्रिया "संपत्ति जोड़ता हूं, तो यह जाता है और शिकायत करता है ।? अगले "अज्ञात" संपत्ति

"HasColumnName" के रूप में मैं इस कोड की पहली संग्रहीत प्रक्रिया एफई की धाराप्रवाह शैली में की उम्मीद/यह चाहते काम नहीं करता है


अद्यतन:

उपयोग करने की कोशिश डेटाअनोटेशन (घटक मॉडेल से कुंजी, और एंटर से कॉलम yFramework) ... आला

public class UnitEntity : IUnit 
{ 
    [Key] 
    [Column("UT_ID")] 
    public Int16 UnitId { get; set; } 
    public string Name { get; set; } 

डेटाबेस समान नामकरण के साथ DBUnitEntity के लिए कोई भी EntityTypeConfiguration की आवश्यकता को दूर किया था कि (अर्थात बस [कुंजी] विशेषता जोड़ना), लेकिन उस संपत्ति के नाम के साथ इकाई के लिए कुछ भी नहीं किया जो डेटाबेस से मेल नहीं खाता (पहले की तरह ही त्रुटि)।

मुझे मॉडल में घटक मॉडेल एनोटेशन का उपयोग करने में कोई फर्क नहीं पड़ता है, लेकिन मैं वास्तव में मॉडल में एंटीटीफ्रेमवर्क एनोटेशन का उपयोग नहीं करना चाहता हूं अगर मैं इसे मदद कर सकता हूं (मॉडल को किसी विशिष्ट डेटा से जोड़ना नहीं चाहता पहुँच ढांचा)

+0

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

+1

एक ही मुद्दे पर आया लेकिन ईएफ 6 के साथ आया। क्या कोई उपर्युक्त ईएफ 6 का उपयोग करने में सक्षम है? –

उत्तर

22

Entity Framework Code First पुस्तक से (पेज 155):

SQLQuery विधि हमेशा संपत्ति के नाम के आधार स्तंभ करने वाली संपत्ति मिलान प्रयास करता है ... कोई भी उस स्तंभ करने वाली प्रॉपर्टी का नाम मेल खाते में कोई मैपिंग नहीं लेता है। उदाहरण के लिए, यदि आपने DestinationId प्रॉपर्टी को गंतव्य तालिका में आईडी नामक कॉलम पर मैप किया था, तो SqlQuery विधि इस मैपिंग का उपयोग नहीं करेगी।

इसलिए संग्रहीत प्रक्रिया को कॉल करते समय आप मैपिंग का उपयोग नहीं कर सकते हैं। एक वर्कअराउंड आपके संग्रहित प्रक्रिया को प्रत्येक कॉलम के लिए उपनाम के साथ परिणाम लौटने के लिए संशोधित करना है जो आपके ऑब्जेक्ट गुणों के नाम से मेल खाएगा।

Select UT_STR_AD as Address From SomeTable आदि

+1

मैं संदर्भ की सराहना करता हूं - इस तरह कुछ भी खोज और पढ़ने में कुछ नहीं देखा था। मेरी इच्छा है कि कुछ माइक्रोसॉफ्ट संदर्भ थे कि यह मामला था, क्योंकि यह counterintuitive लगता है। – babernethy

+1

आपका स्वागत है, पुस्तक में वह यह भी कहती है कि संग्रहित प्रक्रियाओं के लिए मैपिंग समर्थन योजना ढांचे की भविष्य की रिलीज में योजनाबद्ध है :) – Sergey

+0

ऐसा कहने के लिए बेकार हो सकता है, लेकिन यह एक कस्टम क्वेरी के लिए भी सच है। ऐसा लगता है कि मैपिंग को नजरअंदाज कर दिया गया है। – Tipx

3

इस इकाई की रूपरेखा का उपयोग नहीं है, लेकिन यह dbcontext से उत्पन्न कर रहा है। मैंने इंटरनेट पर घंटों के घंटों तक घंटों बिताए हैं और कुछ भी नहीं के लिए डॉट पीक का उपयोग कर रहे हैं। मैंने कुछ पढ़ा जहां SqlQueryRaw के लिए ColumnAttribute को अनदेखा किया गया है। लेकिन मैंने प्रतिबिंब, जेनेरिक, एसक्यूएल डेटारेडर और एक्टिवेटर के साथ कुछ तैयार किया है। मैं इसे कुछ अन्य प्रोसेस पर परीक्षण करने जा रहा हूं। यदि कोई अन्य त्रुटि जांच है जिसमें अंदर जाना चाहिए, तो टिप्पणी करें।

public static List<T> SqlQuery<T>(DbContext db, string sql, params object[] parameters) 
    { 

     List<T> Rows = new List<T>(); 
     using (SqlConnection con = new SqlConnection(db.Database.Connection.ConnectionString)) 
     { 
      using (SqlCommand cmd = new SqlCommand(sql, con)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       foreach (var param in parameters) 
        cmd.Parameters.Add(param); 
       con.Open(); 
       using (SqlDataReader dr = cmd.ExecuteReader()) 
       { 
        if (dr.HasRows) 
        { 
         var dictionary = typeof(T).GetProperties().ToDictionary(
        field => CamelCaseToUnderscore(field.Name), field => field.Name); 
         while (dr.Read()) 
         { 
          T tempObj = (T)Activator.CreateInstance(typeof(T)); 
          foreach (var key in dictionary.Keys) 
          { 
           PropertyInfo propertyInfo = tempObj.GetType().GetProperty(dictionary[key], BindingFlags.Public | BindingFlags.Instance); 
           if (null != propertyInfo && propertyInfo.CanWrite) 
            propertyInfo.SetValue(tempObj, Convert.ChangeType(dr[key], propertyInfo.PropertyType), null); 
          } 
          Rows.Add(tempObj); 
         } 
        } 
        dr.Close(); 
       } 
      } 
     } 
     return Rows; 
    } 

    private static string CamelCaseToUnderscore(string str) 
    { 
     return Regex.Replace(str, @"(?<!_)([A-Z])", "_$1").TrimStart('_').ToLower(); 
    } 

यह भी जानने के लिए कुछ है कि हमारी सभी संग्रहित प्रोसेस लोअरकेस अंडरस्कोर को सीमित करती हैं। CamelCaseToUnderscore विशेष रूप से इसके लिए बनाया गया है।

अब BigDeal आप यह इतना

Namespace.SqlQuery<YourObj>(db, "name_of_stored_proc", new SqlParameter("@param",value),,,,,,,); 
1

की तरह कॉल करने के लिए उदाहरण के लिए "DeadlyChambers" द्वारा पोस्ट की गई महान है सक्षम होना चाहिए big_deal को

मैप कर सकते हैं, लेकिन मैं उदाहरण का विस्तार करने के शामिल करना चाहते हैं ColumnAttribute जिसे आप क्लास प्रॉपर्टी में SQL फ़ील्ड को मैप करने के लिए गुणों में जोड़ने के लिए ईएफ के साथ उपयोग कर सकते हैं।

पूर्व।

[Column("sqlFieldName")] 
public string AdjustedName { get; set; } 

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

/// <summary> 
/// WARNING: EF does not use the ColumnAttribute when mapping from SqlQuery. So this is a "fix" that uses "lots" of REFLECTION 
/// </summary> 
/// <typeparam name="T"></typeparam> 
/// <param name="database"></param> 
/// <param name="sqlCommandString"></param> 
/// <param name="modelPropertyName_sqlPropertyName">Model Property Name and SQL Property Name</param> 
/// <param name="sqlParameters">SQL Parameters</param> 
/// <returns></returns> 
public static List<T> SqlQueryMapped<T>(this System.Data.Entity.Database database, 
    string sqlCommandString, 
    Dictionary<string,string> modelPropertyName_sqlPropertyName, 
    params System.Data.SqlClient.SqlParameter[] sqlParameters) 
{ 
    List<T> listOfT = new List<T>(); 

    using (var cmd = database.Connection.CreateCommand()) 
    { 
     cmd.CommandText = sqlCommandString; 
     if (cmd.Connection.State != System.Data.ConnectionState.Open) 
     { 
      cmd.Connection.Open(); 
     } 

     cmd.Parameters.AddRange(sqlParameters); 

     using (var dataReader = cmd.ExecuteReader()) 
     { 
      if (dataReader.HasRows) 
      { 
       // HACK: you can't use extension methods without a type at design time. So this is a way to call an extension method through reflection. 
       var convertTo = typeof(GenericExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mi => mi.Name == "ConvertTo").Where(m => m.GetParameters().Count() == 1).FirstOrDefault(); 

       // now build a new list of the SQL properties to map 
       // NOTE: this method is used because GetOrdinal can throw an exception if column is not found by name 
       Dictionary<string, int> sqlPropertiesAttributes = new Dictionary<string, int>(); 
       for (int index = 0; index < dataReader.FieldCount; index++) 
       { 
        sqlPropertiesAttributes.Add(dataReader.GetName(index), index); 
       } 

       while (dataReader.Read()) 
       { 
        // create a new instance of T 
        T newT = (T)Activator.CreateInstance(typeof(T)); 

        // get a list of the model properties 
        var modelProperties = newT.GetType().GetProperties(); 

        // now map the SQL property to the EF property 
        foreach (var propertyInfo in modelProperties) 
        { 
         if (propertyInfo != null && propertyInfo.CanWrite) 
         { 
          // determine if the given model property has a different map then the one based on the column attribute 
          string sqlPropertyToMap = (propertyInfo.GetCustomAttribute<ColumnAttribute>()?.Name ?? propertyInfo.Name); 
          string sqlPropertyName; 
          if (modelPropertyName_sqlPropertyName!= null && modelPropertyName_sqlPropertyName.TryGetValue(propertyInfo.Name, out sqlPropertyName)) 
          { 
           sqlPropertyToMap = sqlPropertyName; 
          } 

          // find the SQL value based on the column name or the property name 
          int columnIndex; 
          if (sqlPropertiesAttributes.TryGetValue(sqlPropertyToMap, out columnIndex)) 
          { 
           var sqlValue = dataReader.GetValue(columnIndex); 

           // ignore this property if it is DBNull 
           if (Convert.IsDBNull(sqlValue)) 
           { 
            continue; 
           } 

           // HACK: you can't use extension methods without a type at design time. So this is a way to call an extension method through reflection. 
           var newValue = convertTo.MakeGenericMethod(propertyInfo.PropertyType).Invoke(null, new object[] { sqlValue }); 

           propertyInfo.SetValue(newT, newValue); 
          } 
         } 
        } 

        listOfT.Add(newT); 
       } 
      } 
     } 
    } 

    return listOfT; 
} 
संबंधित मुद्दे