I have generated my POCO entities using POCO Generator, I have more than 150+ tables in my database. I am sharing POCO entities all across the application layers including t
I use following calls to flatten the EntityFramwork POCO entities before return it from WCF service. It will cut the circular children object based on maxLevel value.
/// <summary>
/// Flattern one custom POCO entity.
/// Work for resolve the circular reference issues in Entity Framework Code First POCO entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <param name="maxLevel"></param>
/// <param name="currLevel"></param>
/// <returns></returns>
public static T EntityFlatten<T>(this T entity, int maxLevel = 1, int currLevel = 1) where T : class
{
if (entity != null)
{
var myType = entity.GetType();
var myAssembly = myType.Assembly;
var props = myType.GetProperties();
if (props != null)
{
// Walk through all properties defined in the POCO entity.
foreach (var prop in props)
{
Type typeOfProp = prop.PropertyType;
//
// If the type is from my assembly == custom type
// include it, but flatten its properties.
// It is one custom POCO entity.
//
if (typeOfProp.Assembly == myAssembly)
{
if (currLevel < maxLevel)
{
prop.SetValue(entity, EntityFlatten(prop.GetValue(entity, null), maxLevel, currLevel+1));
}
else
{
prop.SetValue(entity, null);
}
}
else
{
//It should be POCO collection property
if (typeOfProp.Namespace == "System.Collections.Generic")
{
if (currLevel < maxLevel)
{
var originalList = prop.GetValue(entity, null) as IList;
if (originalList != null && originalList.Count>0)
{
for (int i=0; i<originalList.Count;i++)
{
var item = originalList[i].EntityFlatten(maxLevel, currLevel);
originalList[i] = item;
i++;
}
prop.SetValue(entity, originalList);
}
}
else
{
prop.SetValue(entity, null);
}
}
}
}
}
return entity;
}
else
return null;
}
/// <summary>
/// Flatten the POCO entities collection.
/// Work for resolve the circular reference issues in Entity Framework Code First POCO entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entities"></param>
/// <param name="maxLevel"></param>
/// <param name="currentLevel"></param>
/// <returns></returns>
public static IList<T> EntitiesFlatten<T>(this IList<T> entities, int maxLevel = 1, int currentLevel = 1) where T : class
{
if (entities != null)
{
var list = entities as IList<T>;
for (int i = 0; i < list.Count; i++)
{
T entity = list[i];
entity = entity.EntityFlatten<T>(maxLevel, currentLevel);
}
return list;
}
else
return null;
}
I had the same problem and resolved it by excluding the navigation property back to the parent from the DataContract
[DataContract]
public partial class Parent
{
[Key]
[DataMember]
public virtual int ParentId { get; set; }
[DataMember]
public virtual string ParentName { get; set; }
[DataMember]
public virtual Child Child { get; set; }
}
[DataContract]
public partial class Child
{
[Key]
[DataMember]
public virtual int ChildId { get; set; }
[DataMember]
public virtual string ChildName { get; set; }
public virtual List<Parent> Parents {get; set;}
}
You already mentioned the approach, but I use this attribute
public class ReferencePreservingDataContractFormatAttribute : Attribute, IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyClientBehavior(description, proxy);
}
public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}
public void Validate(OperationDescription description)
{
}
#endregion
}
} ...and reference on an operation on the Service like so;
[OperationContract]
[ReferencePreservingDataContractFormat]
IList<SomeObject> Search(string searchString);
FYI - would like to give credit where it's due, but did not record where I originally saw the above approach.
Edit:
I believe source of the code is from this blog post.