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)?
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.
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.
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
Check out ServiceStack.Text library from GitHub and (of course) reference it in your MVC project instead of DLL.
Add this to ServiceStack.Text/JsConfig class:
// Added properties for global excluding
public static Type[] GlobalExcludedBaseTypes;
public static string[] GlobalExcludedProperties;
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();
}
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.
You can use ScriptIgnore attribute on your model:
public class Item
{
[ScriptIgnore]
public Item ParentItem { get; set; }
}