EF 4 Entity mit Json.Net Serialisieren

Kleiner Trick zum Serialisieren von Entity-Framework Objekten mit Newtonsoft Json.Net

Das Entity-Framework generiert für die Entity-Klassen Proxies, die den Zugriff auf virtuelle Properties und Collections kapseln. Diese Proxy-Klassen werden per Reflection.Emit zur Laufzeit erzeugt. Hiermit kann Newtonsoft Json.Net leider nichts anfangen.

Diese ausgezeichnete Bibliothek zur Serialisierung von .Net Objekten nach Json und vice-versa bietet aber vielseitige Erweiterungsmöglichkeiten, so dass auch das oben genannte Problem umschifft werden kann.

Um also zu verhindern, dass Json.Net versucht eine Property eine Proxy-Klasse zu serialisieren (was zu einer Exception führt), dürfen nur Member-Variablen mit bekannten Typen beachtet werden. Hierzu kann in den JsonSerializerSettings ein IContractResolver angegeben werden:

var settings = new JsonSerializerSettings()
{
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    TypeNameHandling = TypeNameHandling.Objects,
    ContractResolver = new EFContractResolver()
};
var json = JsonConvert.SerializeObject(query, Formatting.Indented, settings);

Der EFContractResolver verhält sich im grossen und ganzen wie der Json.Net DefaultContractResolver. Dieser enthält eine überschreibbare Methode GetSerializableMembers in der diejenigen Member eines Typs eingeschlossen werden, die in der Json-Ausgabe wieder aufsuchen sollen. Da wir die zusätzlichen Member des Proxies ignorieren wollen, nutzen wir die Fähigkeit der EF-ObjectContext auf alle Entity-Proxy-Abbildungen innerhalb einer App-Domain zuzugreifen:

public class EFContractResolver : DefaultContractResolver
{
    protected override List GetSerializableMembers(Type proxyType)
    {
        var knownProxyTypes = ObjectContext.GetKnownProxyTypes();

        if (!knownProxyTypes.Contains(proxyType))
            return base.GetSerializableMembers(proxyType);

        var entityType = ObjectContext.GetObjectType(proxyType);
        return base.GetSerializableMembers(entityType);
    }
}

An dieser Stelle sind natürlich noch andere Filter-Möglichkeiten denkbar, z.B. Sichtbarkeit per Display-Attribut oder die Zugriffssteuerung per Authorize-Attribut.