3

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)?

nikib3ro
  • 20,366
  • 24
  • 120
  • 181

3 Answers3

3

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.

Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Well, for me they would be great DTO's if I could just strip unnecessary information automatically. If I do special DTO types - that means more maintenance - then if I change database structure I would need to duplicate that change in my DTO's also. I get that you may want that extra layer sometimes, but for my purposes it would just introduce additional complexity unnecessary. Anyways, I appreciate your response since you're the expert on the subject. So, no auto-luck for me? Switch to Json.NET? You guys don't have "Generate classes from DB" in ServiceStack? – nikib3ro Feb 21 '13 at 17:29
  • 1
    The DTO is de-coupled from the Data Model, so you only have to update them when you want to include fields you want to return, it's also insulated and less likely to break your clients when you make changes on db concerns. Whilst I don't recommend the pit-of-failure you're heading down for using EF data models as DTOs (i.e. you would not be here if you used DTOs :), I have provided a link showing how to ignore properties in serialization with ServiceStack's serializers. – mythz Feb 21 '13 at 17:36
  • Yeah - just saw that one (JsConfig.ExcludePropertyNames) and started typing comment back - I knew you guys are way too cool not to have something like this covered. Do you have something like that only for types (JsConfig.ExcludePropertyTypes)? And also, I would love if this one: JsConfig.ExcludePropertyNames = new[] { "EntityKey", "EntityState" }; worked, but it doesn't :(. Any suggestions (or I'll need to fork source on Github)? And thanks for giving me all the options - not being like "NO, IT CAN'T BE DONE". – nikib3ro Feb 21 '13 at 17:44
  • 1
    Do you have a full code example showing how it doesn't work? Note: it only applies to the type, e.g. you'd never want to use `JsConfig` since it only applies to `object` and not all objects as you might think. – mythz Feb 21 '13 at 17:49
  • Yeah, I was trying to use base type (EntityObject) and expecting it would strip EntityKey&EntityState for all inherited EF objects - I get it - doesn't work that way. Well, it seems I'll either be forced to fork or just switch to Json.NET (this one especially sucks). Please let me know if I'm missing something (like using JsConfig.OnSerializingFn) that could save me time. Thanks for your help! – nikib3ro Feb 21 '13 at 18:03
  • I wanted to add that if you use EF CodeFirst POCO entities, you don't actually need to create new DTO *classes*, you simply need to create new instances that aren't using proxy magic in the background. It still has more maintenance, but not the maintenance of the class - only of the CreateDto implementation. I personally don't want to modify SS source, though I like the generic EF solution. Perhaps if it could be more extensively tested, mythz could add that as an officially supported option? (I think EF could be popular enough to warrant it). –  Apr 01 '15 at 13:19
1

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.

Community
  • 1
  • 1
nikib3ro
  • 20,366
  • 24
  • 120
  • 181
0

You can use ScriptIgnore attribute on your model:

public class Item
{
    [ScriptIgnore]
    public Item ParentItem { get; set; }
}
Jeroen K
  • 10,258
  • 5
  • 41
  • 40