2010-01-13 2 views
13

엔티티 프레임 워크에서 세 가지 클래스가있는 경우.Entity Framework에서 기본 클래스 만 검색

class Base {} 

class Left : Base {} 

class Right : Base {} 

나는 어떤 사람들은 눈치 것처럼 이것은 완전히 관련 상속 유형에 입력 Base의 모든 인스턴스를 반환 DBContext.Bases.ToList();

전화는 큰 상속 구조에 EF의 성능은 적어도 말을 잘하지 않습니다 . 내 프로젝트의 내 실제 쿼리는 한 줄의 엔티티를 반환하기 위해 600 줄 길이이며 생성하는 데 2 ​​초가 걸립니다.

전체 구조에 걸쳐 조인 할 필요가 없으므로 반환 할 형식을 지정하면 쿼리가 훨씬 빠르게 실행됩니다. 예 :

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

그러나 나는 이제 기본 클래스를 반환 로합니다. Unfortunalty) ( DBContext.Bases.ToList과 동일

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

을 수행하고;

WHOLE 상속 구조가 생깁니다 ... 기본 컬렉션을 살펴볼 때 클래스를 반환하는 방법이 있습니까?


내 실제 계정에 로그인하지 못할 죄송합니다

...

어쩌면 내가 (자료, 왼쪽과 오른쪽을 포함하여) 모든 개체를 다시 가지고 싶어 자신을 명확하게 didnt는하지만 난 단지 원하는 데이터베이스에서 실제 Left 및 Right 클래스 인 경우에도 반환 할 기본 클래스입니다.

OFTYPE은 좋은 제안 이었지만 실제베이스 유형이 없기 때문에 모든 엔티티를 필터링합니다. 하지만 기본 형식 개체에서 기본 형식 값을 반환 할 싶어요.

아이디어가 있으십니까? 당신을 가정

+0

이것은 불행하게도, 당신보다 더 열심히이 수도 적어도 LINQ to Entities에서 기대할 수 있습니다. Entity SQL에서는 OFTYPE (ONLY ...)을 사용할 수 있습니다. Alex James는이 작업을 수행하는 방법을 설명합니다 (http://blogs.msdn.com/alexj/archive/2009/09/17/tip-35-how-to-write-oftypeonly-tentity.aspx "팁 35 - OfTypeOnly 작성 방법 <TEntity>() "). –

답변

1

는에있는 하위 클래스를 가정하면, 다음과 같은 신속하고 더러운 예? 내가 현재 다음 LINQ 확장을 사용

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

'GetType()'은 LINQ to Entities에서 지원되지 않습니다. : –

+0

Hmmm ... Strange. ToList()는 반환 형식이 LINQ가 매우 만족할 IEnumerable 형식이었을 것이라고 생각했을 것입니다. GetType()가 지원되지 않는다면 조금 더 놀랄 것입니다. 여기에서 실수를하거나 프레임 워크에서 심각한 누락의 예가 무엇입니까? –

+2

아니요, ToList를 놓쳤습니다.하지만 이제는 L2O에 있습니다. L2E) 때문에 DB 서버는 DB 서버에서 행을 수행하는 대신 모든 행을 반환합니다 (OfType ()'). 당신이 제공하는 쿼리는 느리지 만 작동합니다. CLR 메서드는 특정 SQL 변환이 없으면 지원됩니다 (전체 목록은 http://msdn.microsoft.com/en-us/library/bb738681.aspx 참조).'GetType()'이 지원 되었으면 좋겠지 만 'Type'은 많은 기능을 가지고있어서 EF가 할 작은 일은 없을 것입니다. –

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)); 
    } 
} 

사용법 :

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

또한 Linq-to-entities가 아닌 Linq-to-objects가 필요합니다. –

2

가 엔티티 프레임 워크 이해하지만, 키워드 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

위의 답 중 아무 것도 처리하지 않는 것 (즉, 반환 된 열만 필터링하여 기본 형식 열에 만 사용하고 파생 형식 정보가있는 행은 필터링하지 않는다는 질문에 대답하기 위해) 익명 형식으로이 작업을 수행하는 방법은 매우 간단합니다. 세부 사항을 다루는 또 다른 stackoverflow 질문은 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()에 개체 이니셜 라이저 목록을 호출하십시오.

이것은 유형에 특정한 불행한 단점이 있습니다. 여기 사무실에있는 누군가는 일반적인 것을 쓰고 싶습니다. 그래서 저는 곧 돌아올 것이고이 대답을 완전히 일반적인 버전으로 편집 할 것입니다.

EDIT (하지만 생산 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(); 
관련 문제