2010-01-13 16 views
13

से केवल बेस क्लास पुनर्प्राप्त करें यदि मेरे पास इकाई ढांचे में तीन वर्ग हैं।इकाई फ्रेमवर्क

class Base {} 

class Left : Base {} 

class Right : Base {} 

और मैं फोन DBContext.Bases.ToList();

यह Base पूरी तरह से उनके जुड़े विरासत में मिला प्रकार में टाइप के सभी उदाहरणों देता है, के रूप में कुछ लोगों को देखा है, बड़े विरासत संरचनाओं पर एफई के प्रदर्शन कम से कम कहने के लिए महान नहीं है । मेरी परियोजना में मेरी वास्तविक क्वेरी 600 लाइन लंबी है, केवल एक इकाई को वापस करने के लिए और उत्पन्न करने में 2 सेकंड लगती है।

यदि आप इसे बताते हैं कि कौन सा प्रकार वापस लौटना है, तो वे बहुत तेज़ी से चलते हैं, क्योंकि इसे पूरी संरचना में शामिल होने की आवश्यकता नहीं है। जैसे

DBContext.Bases.OfType<Left>.ToList(); 
or 
DBContext.Bases.OfType<Right>.ToList(); 

हालांकि मैं अब करना चाहते हैं केवल आधार वर्ग लौटने। Unfortunalty रूप DBContext.Bases.ToList ही कर

DBContext.Bases.OfType<Base>.ToList(); 

है();

यह WHOLE विरासत संरचना प्राप्त करता है ... क्या की केवल बेस संग्रह को देखकर कक्षा बेस लौटने पर कोई तरीका है (ईएफ में नया प्रकार बनाये बिना)?


माफ करना, मैं नहीं कर सकते मेरे वास्तविक खाते में प्रवेश ...

हो सकता है कि मैं अपने आप को स्पष्ट करना नहीं था, मैं (बेस, बाएं और दाएं सहित) सभी वस्तुओं वापस लाना चाहते हैं, लेकिन मैं केवल चाहते हैं बेस क्लास को वापस करने के लिए, भले ही डेटाबेस में वे वास्तविक बाएं और दाएं वर्ग हों।

OFTYPE एक अच्छा सुझाव था लेकिन यह मेरी सभी संस्थाओं को फ़िल्टर करता है क्योंकि कोई वास्तविक आधार प्रकार नहीं है। लेकिन मैं बेस टाइप ऑब्जेक्ट में बेस बेस मानों को वापस करना चाहता हूं।

कोई विचार?

+0

यह वह जगह है, दुर्भाग्य से, आप की तुलना में कठिन हो सकता है कम से कम LINQ से इकाइयों में उम्मीद है। इकाई एसक्यूएल में आप 'OFTYPE (केवल ...)' का उपयोग कर सकते हैं। एलेक्स जेम्स बताते हैं कि यह कैसे करें [इस टिप में] (http://blogs.msdn.com/alexj/archive/2009/09/17/tip-35-how-to-write-oftypeonly-tentity.aspx "युक्ति 35 - ऑफ टाइप टाइप कैसे करें <TENTITY>() ")। –

उत्तर

1

आप यह मानते हुए LINQ का उपयोग करने में सक्षम हैं, तो आपको निम्न त्वरित और गंदी उदाहरण ?:

var result = from item in DBContext.Bases.ToList() 
      where (!item.GetType().IsSubclassOf(typeof(Base))) 
      select item; 
+1

'GetType()' दुर्भाग्यवश, LINQ से इकाइयों में समर्थित नहीं है। :( –

+0

हमम ... अजीब। मैंने सोचा होगा कि एक ToList() का मतलब होगा कि रिटर्न प्रकार एक आईनेमरेबल के रूप में होता, जो LINQ बहुत खुश होगा। और GetType के मुद्दे के लिए() समर्थित नहीं है, यह थोड़ा और खतरनाक है। क्या मैं यहां गलत हूं, या क्या यह ढांचे में गंभीर चूक का एक उदाहरण है? –

+2

नहीं, मुझे 'ToList' याद आया। लेकिन अब आप L2O में हैं (बजाय एल 2 ई), इसलिए डीबी सर्वर डीबी सर्वर पर इसे करने के बजाए सभी पंक्तियों को वापस करता है (जैसा कि 'ऑफ टाइप () 'करता है)। आपके द्वारा दी गई क्वेरी धीरे-धीरे काम करेगी।' GetType()' समर्थित नहीं है क्योंकि नहीं सीएलआर विधि तब तक समर्थित है जब तक इसके लिए कोई विशिष्ट SQL अनुवाद न हो; http://msdn.microsoft.com/en-us/library/bb738681.aspx पूरी सूची के लिए देखें। मेरी इच्छा है कि 'GetType() 'समर्थित थे, लेकिन 'टाइप' में बहुत सी विशेषताएं हैं, इसलिए ईएफ के लिए यह कोई छोटा काम नहीं होगा। –

0

मैं वर्तमान में निम्नलिखित LINQ एक्सटेंशन का उपयोग की तर्ज पर कुछ इस्तेमाल कर सकते हैं, उप-वर्गों संभालने में स्थित हैं एक ही असेंबली।

public static class MyLinqExtensions 
{ 
    public static IQueryable<T> OfTypeOnly<T>(this IQueryable<T> query) 
    { 
     Type type = typeof (T); 
     IEnumerable<Type> derivedTypes = Assembly 
      .GetAssembly(type) 
      .GetTypes() 
      .Where(t => t.IsSubclassOf(type)); 

     return query.ExceptTypes(derivedTypes.ToArray()); 
    } 

    public static IQueryable<T> ExceptTypes<T>(this IQueryable<T> query, params Type[] excludedTypes) 
    { 
     if (excludedTypes == null) 
      return query; 

     return excludedTypes.Aggregate(query, 
      (current, excludedType) => current.Where(entity => entity.GetType() != excludedType)); 
    } 
} 

उपयोग:

var bases = DBContext.Bases.OfTypeOnly<Base>(); 
+1

यह लिंक-टू-ऑब्जेक्ट्स को लिंक-टू-एंटिटीज़ की भी आवश्यकता नहीं है। –

2

GetType() इकाई की रूपरेखा से समझा नहीं गया है, लेकिन कीवर्ड is काम करता है। इस तरह आप एक अभिव्यक्ति बना सकते हैं और इसे अपनी क्वेरी पर लागू कर सकते हैं। यहां कोड को एक एक्सटेंशन विधि जोड़ने के लिए EF5 + के लिए काम करना चाहिए जिसे आप कॉल कर सकते हैं: query.OfOnlyType<Base, SubTypeWithDescendants>()। (या एक ही दो प्रकार तर्क के साथ आप, की जरूरत है अगर मेरे पदानुक्रम है कि हालांकि तुलना में अधिक जटिल है)

public static IQueryable<ReturnType> OfOnlyType<ReturnType, QueryType> 
     (this IQueryable<QueryType> query) 
     where ReturnType : QueryType { 

    // Look just for immediate subclasses as that will be enough to remove 
    // any generations below 
    var subTypes = typeof(ReturnType).Assembly.GetTypes() 
     .Where(t => t.IsSubclassOf(typeof(ReturnType))); 
    if (subTypes.Count() == 0) { return query.OfType<ReturnType>(); } 

    // Start with a parameter of the type of the query 
    var parameter = Expression.Parameter(typeof(ReturnType)); 

    // Build up an expression excluding all the sub-types 
    Expression removeAllSubTypes = null; 
    foreach (var subType in subTypes) { 
     // For each sub-type, add a clause to make sure that the parameter is 
     // not of this type 
     var removeThisSubType = Expression.Not(Expression 
      .TypeIs(parameter, subType)); 

     // Merge with the previous expressions 
     if (removeAllSubTypes == null) { 
      removeAllSubTypes = removeThisSubType; 
     } else { 
      removeAllSubTypes = Expression 
       .AndAlso(removeAllSubTypes, removeThisSubType); 
     } 
    } 

    // Convert to a lambda (actually pass the parameter in) 
    var removeAllSubTypesLambda = Expression 
     .Lambda(removeAllSubTypes, parameter); 

    // Filter the query 
    return query 
     .OfType<ReturnType>() 
     .Where(removeAllSubTypesLambda as Expression<Func<ReturnType, bool>>); 
} 

मैं केवल एक कोड की पहली मॉडल के साथ EF6.1 पर यह परीक्षण किया है। यह Alex James' tip 35 से भारी उधार लेता है।

1

इस प्रश्न का उत्तर देने के लिए कि उपरोक्त में से कोई भी उत्तर का ख्याल रखने की प्रतीत नहीं होता है (यानी, हम केवल कॉलम कॉलम को केवल बेस प्रकार कॉलम ही फ़िल्टर कर रहे हैं, लेकिन पंक्तियों को फ़िल्टर करने वाली पंक्तियों को फ़िल्टर नहीं कर रहे हैं), अनाम प्रकारों के साथ ऐसा करने का एक काफी सरल तरीका है। विशिष्टताओं से निपटने वाले दूसरे स्टैक ओवरफ्लो प्रश्न के लिए here देखें।

db.BaseTypes.Select(o => new { Prop1 = o.Prop1, Prop2 = o.Prop2, ....}) 
.AsEnumerable() 
.Select(a => new BaseType() { Prop1 = a.Prop1, Prop2 = a.Prop2, ...}); 

Linq करने वाली संस्थाओं गुमनाम वस्तुओं की एक सूची वापस आ जाएगी, वापस Linq करने वाली वस्तुओं को .AsEnumerable() रिटर्न आप और करने की अनुमति देता है, जबकि:

विचार कुछ इस तरह करना है ऑब्जेक्ट प्रारंभकर्ता सूची के साथ new BaseType() पर कॉल करें।

इस प्रकार के विशिष्ट होने का दुर्भाग्यपूर्ण नकारात्मक पक्ष है। कार्यालय में कोई भी एक सामान्य लिखित चाहता है, इसलिए मैं जल्द ही वापस आऊंगा और इस उत्तर को पूरी तरह से सामान्य संस्करण के साथ संपादित करूंगा।

संपादित करें (परीक्षण किया है, लेकिन उत्पादन EntityFramework में नहीं): SelectDynamic कोड के लिए this answer को

धन्यवाद।

public static class QueryableExtensions { 

    /// <summary> 
    /// Constructs a query that only selects the columns that are actually in the type <typeparamref name="T"/> as public properties. 
    /// 
    /// Useful for inherited types when you only want the base type information. 
    /// </summary> 
    /// <remarks> 
    /// This function materializes the query. You'll want to call the where clauses BEFORE this call (since it is an optimization). 
    /// </remarks> 
    /// <typeparam name="T">Entity type.</typeparam> 
    /// <param name="query">Source query.</param> 
    /// <returns>An IEnumerable of items of type <typeparamref name="T"/>.</returns> 
    public static IEnumerable<T> FilterColumnsByType<T>(this IQueryable<T> query) where T : new() { 
     Type type = typeof(T); 
     List<string> selectedProps = type.GetProperties().Select(p => p.Name).ToList(); 

     Tuple<IQueryable, Type> anonObjectTypePair = query.SelectDynamicAndType(selectedProps); 
     IQueryable anonObjects = anonObjectTypePair.Item1; 
     Type anonType = anonObjectTypePair.Item2; 

     return anonObjects.Cast<object>().AsEnumerable().Select(ob => { 
      var ret = new T(); 
      selectedProps.ForEach(p => 
       type.GetProperty(p).SetValue(ret, anonType.GetField(p).GetValue(ob))); 
      return ret; 
     }); 
    } 

    /// <summary> 
    /// Constructs a query that selects only the <paramref name="propNames"/> given and returns an <see cref="IQueryable"/> of dynamic objects with only the selected fields. 
    /// 
    /// Also returns the type information of the dynamic objects. 
    /// </summary> 
    /// <param name="source">Source query.</param> 
    /// <param name="propNames">The list of properties names to select.</param> 
    /// <returns>A query of anonymous types defined by the supplied <paramref name="propNames"/> and the actual <see cref="Type"/> used to construct anonymous type.</returns> 
    public static Tuple<IQueryable, Type> SelectDynamicAndType(this IQueryable source, IEnumerable<string> propNames) { 
     Dictionary<string, PropertyInfo> sourceProperties = propNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name)); 
     Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values); 

     ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t"); 
     IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>(); 

     Expression selector = Expression.Lambda(Expression.MemberInit(
       Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem); 

     return Tuple.Create(source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType }, 
           Expression.Constant(source), selector)), dynamicType); 
    } 


    /// <summary> 
    /// Constructs a query that selects only the <paramref name="propNames"/> given and returns an <see cref="IQueryable{dynamic}"/> of dynamic objects with only the selected fields. 
    /// </summary> 
    /// <param name="source">Source query.</param> 
    /// <param name="propNames">The list of properties names to select.</param> 
    /// <returns>A query of anonymous types defined by the supplied <paramref name="propNames"/>.</returns> 
    public static IQueryable<dynamic> SelectDynamic(this IQueryable source, IEnumerable<string> propNames) { 
     return source.SelectDynamicAndType(propNames).Item1.Cast<dynamic>(); 
    } 

    static class LinqRuntimeTypeBuilder { 
     private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" }; 
     private static ModuleBuilder moduleBuilder = null; 
     private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>(); 

     static LinqRuntimeTypeBuilder() { 
      moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name); 
     } 

     private static string GetTypeKey(Dictionary<string, Type> fields) { 
      string key = string.Empty; 
      foreach (var field in fields.OrderBy(kvp => kvp.Key).ThenBy(kvp => kvp.Value.Name)) 
       key += field.Key + ";" + field.Value.Name + ";"; 

      return key; 
     } 

     private static Type GetDynamicType(Dictionary<string, Type> fields) { 
      if (null == fields) 
       throw new ArgumentNullException("fields"); 
      if (0 == fields.Count) 
       throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition"); 

      try { 
       Monitor.Enter(builtTypes); 
       string className = GetTypeKey(fields); 

       if (builtTypes.ContainsKey(className)) 
        return builtTypes[className]; 

       TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable); 

       foreach (var field in fields) 
        typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public); 

       builtTypes[className] = typeBuilder.CreateType(); 

       return builtTypes[className]; 
      } catch (Exception ex) { 
       //log.Error(ex); 
       Console.WriteLine(ex); 
      } finally { 
       Monitor.Exit(builtTypes); 
      } 

      return null; 
     } 

     public static Type GetDynamicType(IEnumerable<PropertyInfo> fields) { 
      return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType)); 
     } 
    } 
} 
0

आप DbSet.SqlQuery उपयोग कर सकते हैं:

DBContext.Bases.SqlQuery("select * from BaseTable").AsNoTracking().ToList(); 

पता है कि .AsNoTracking() का उपयोग नहीं अभी या बाद में गर्म पानी में आप मिल जाएगा रहो (यदि वहाँ पहले से ही संदर्भ आप अद्वितीय कुंजी मिल जाएगा में लोड प्रकार प्राप्त कर रहे हैं उल्लंघनों/अपवाद तत्काल)।

0

प्रदर्शन मतभेदों के बारे में सुनिश्चित नहीं हैं, लेकिन मैं कल्पना कर सकता है कि इस सभी पंक्तियों लोड हो रहा है (जब बहुत सी पंक्तियाँ DB में हैं) की तुलना में तेजी होगा:

List<int> ids = DBContext.Rights.Select(x => x.Id).ToList(); 
ids.AddRange(DBContext.Lefts.Select(x => x.Id).ToList()); 
var bases = DBContext.Bases.Where(x => !ids.Contains(x.Id)).ToList(); 
संबंधित मुद्दे