2016-06-22 5 views
3

nehme ich an Benutzer geladen wurde mit Tags faul geladen:Wie zu testen, ob eine faule geladenes Kind Sammlung hat

public class User 
{ 
    public int UserId { get; set; } 

    public virtual ICollection<Tag> Tags { get; set; } 
} 

Wenn ich einen Benutzer auf diese Weise bekommen, ich weiß, dass die Tags nicht geladen werden:

User myUser; 
using (var context = new MyContext()) 
{ 
    myUser = context.Users.Find(4); 
} 

Wie teste ich die Tags Sammlung Präsenz außerhalb der using Klausel?

if (myUser.Tags == null) // throws an ObjectDisposedException 

Ich könnte versuchen/fangen, aber es muss einen besseren Weg geben.

+0

@Tuco Ich will nicht, sie zu laden, nur testen, ob sie geladen sind. Wenn ich mehrere verschiedene Möglichkeiten habe, diese Entity zu laden (einige mit, andere ohne 'Include'), sollte sich irgendein externer Code an die verfügbaren untergeordneten Sammlungen anpassen können. – Mart

Antwort

2

Die einzige Möglichkeit, die ich mir vorstellen kann, ist ein nicht virtueller Aufruf (ähnlich wie wenn Sie base.Something in einer abgeleiteten Klasse tun) zu der Klasseneigenschaft Getter. Da es keine Möglichkeit, das zu tun, mit reinen C# oder Reflexion, habe ich mit der folgenden Hilfsmethode endete die Call IL-Befehl anstelle der normalen Callvirt auszusenden System.Reflection.Emit LCG (leichte Codeerzeugung) verwendet:

using System; 
using System.Linq.Expressions; 
using System.Reflection; 
using System.Reflection.Emit; 

public static class Utils 
{ 
    public static TValue GetClassValue<TSource, TValue>(this TSource source, Expression<Func<TSource, TValue>> selector) 
     where TSource : class 

    { 
     Func<TSource, TValue> getValue = null; 
     if (source.GetType() != typeof(TSource)) 
     { 
      var propertyAccessor = selector.Body as MemberExpression; 
      if (propertyAccessor != null) 
      { 
       var propertyInfo = propertyAccessor.Member as PropertyInfo; 
       if (propertyInfo != null) 
       { 
        var getMethod = propertyInfo.GetGetMethod(); 
        if (getMethod != null && getMethod.IsVirtual) 
        { 
         var dynamicMethod = new DynamicMethod("", typeof(TValue), new[] { typeof(TSource) }, typeof(Utils), true); 
         var il = dynamicMethod.GetILGenerator(); 
         il.Emit(OpCodes.Ldarg_0); 
         il.EmitCall(OpCodes.Call, getMethod, null); 
         il.Emit(OpCodes.Ret); 
         getValue = (Func<TSource, TValue>)dynamicMethod.CreateDelegate(typeof(Func<TSource, TValue>)); 
        } 
       } 
      } 
     } 
     if (getValue == null) 
      getValue = selector.Compile(); 
     return getValue(source); 
    } 
} 

Es kann sowohl für wie diese Einzel- und Sammeltyp Navigationseigenschaften verwendet werden:

if (myUser.GetClassValue(x => x.Tags) == null) 
+0

Beeindruckend. Also gibt es den tatsächlichen Wert zurück wenn es geladen wurde und sonst Null? Können Sie die Kosten für diesen Anruf im Vergleich zu einem Versuch, den Wert zu erhalten, einschätzen? – Mart

+0

Ja - Sie können es versuchen :) Und ja, es ist teuer, also, wenn die Leistung betrifft, dann würde es einige Delegate Caching benötigen. –

0

eine andere Lösung, die eine naive try/catch, nicht sicher über die Leistung im Vergleich zu den anderen Antwort mit:

using System; 

public static class EntityFrameworkExtensions 
{ 
    public static bool IsCollectionLoaded<TSource, TValue>(this TSource source, Func<TSource, TValue> selector) 
     where TSource : class 
    { 
     try 
     { 
      return (selector(source) != null); 
     } 
     catch (ObjectDisposedException) 
     { 
      return false; 
     } 
    } 
} 

Nutzung:

if (myUser.IsCollectionLoaded(x => x.Tags))