Do not serialize Entity Framework class references

2020-02-26 13:59发布

问题:

As most of the people I've also ran into a problem of circular reference error when serializing (terrible) EF objects to JSON. Doing db.Detach(efObject) helps - but I still get garbage like "EntityKey" outputted.

So I was wondering if there is an option (through JsConfig?) to tell serializer to ignore Properties either through name (EntityKey) or through type (EntityReference<T> or EntityCollection<T>)?

Or will I be forced to ditch EF alltogether and switch to something better (I do not want to manually define ORM classes - I want them automatically generated from DB)?

回答1:

You shouldn't try to re-use Entity Framework types as DTO's since they are by design poor substitutes for DTOs. You should instead map them to special purpose DTO types using ServiceStack's built-in TranslateTo/PopulateFrom mappers (or AutoMapper) and return those.

With that said, use IgnoreDataMember or specify DataMembers on properties you want serialized.



回答2:

DISCLAIMER: I am currently doing prototyping and for me convenience matters more than robustness of solution. So, what I've done is not a good way to go - I am copy-pasting it here just in case somebody else is in same position and wants an easy (not the best) way out. You've been warned.

  1. I presume you are already using ServiceStack.Text in your MVC project. If that's not the case, follow instructions from this QA: ASP.NET MVC Json DateTime Serialization conversion to UTC

  2. Check out ServiceStack.Text library from GitHub and (of course) reference it in your MVC project instead of DLL.

  3. Add this to ServiceStack.Text/JsConfig class:

    // Added properties for global excluding
    public static Type[] GlobalExcludedBaseTypes;
    public static string[] GlobalExcludedProperties;
    
  4. Change static TypeConfig() in ServiceStack.Text/TypeConfig class:

    static TypeConfig()
    {
        config = new TypeConfig(typeof(T));
    
        var excludedProperties = JsConfig<T>.ExcludePropertyNames ?? new string[0];
        var excludedGlobalBaseTypes = JsConfig.GlobalExcludedBaseTypes ?? new Type[0];
        var excludedGlobalProperties = JsConfig.GlobalExcludedProperties ?? new string[0];
    
        IEnumerable<PropertyInfo> properties = null;
    
        if (excludedProperties.Any() || excludedGlobalBaseTypes.Any() || excludedGlobalProperties.Any())
        {
            properties = config.Type.GetSerializableProperties().
                Where(x => !excludedProperties.Contains(x.Name) && !excludedGlobalProperties.Contains(x.Name) && !excludedGlobalBaseTypes.Contains(x.PropertyType.BaseType));
        }
        else
        {
            properties = config.Type.GetSerializableProperties();
        }
    
        Properties = properties.Where(x => x.GetIndexParameters().Length == 0).ToArray();
        Fields = config.Type.GetSerializableFields().ToArray();
    }
    
  5. In your Global.asax just add these two lines:

    JsConfig.GlobalExcludedProperties = new[] { "EntityKey", "EntityState" };
    JsConfig.GlobalExcludedBaseTypes = new[] { typeof(RelatedEnd), typeof(EntityReference) };
    

That's it - EntityState & EntityKey will no longer be serialized and you'll no longer need to worry about circular dependencies.

But, again - THIS IS NOT BEST PRACTICE - and as soon as you stabilize your solution be sure to follow what mythz recommended and migrate toward DTO's.



回答3:

You can use ScriptIgnore attribute on your model:

public class Item
{
    [ScriptIgnore]
    public Item ParentItem { get; set; }
}