Circular References and WCF
Asked Answered
L

3

6

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 the client. I have disabled both LazyLoading and ProxyCreation in my context.I am using WCF on top of my data access and business layer.

Now, When I return a poco entity to my client, I get an error saying "Underlying connection was closed" I enabled WCF tracing and found the exact error : Contains cycles and cannot be serialized if reference tracking is disabled.

I Looked at MSDN and found solutions like setting IsReference=true in the DataContract method atttribute but I am not decorating my POCO classes with DataContracts and I assume there is no need of it as well. I won't be calling that as a POCO if I decorate a class with DataContract attribute

Then, I found solutions like applying custom attribute [CyclicReferenceAware] over my ServiceContracts.That did work but I wanted to throw this question to community to see how other people managed this and also why Microsoft didn't give built in support for figuring out cyclic references while serializing POCO classes

Levison answered 11/2, 2011 at 9:51 Comment(3)
it's seems to be a while, Is question too vague..? or there aren't good answers to it?Levison
Question is good and you already have 3 ways to do it - IsReference in DataContract, CyclicReferenceAware and solution from the answer. MS included built in support for figuring out cyclic reference - you just need to trigger it somehow - that's exactly what those approaches are doing. Also it is not always to return POCO's directly - if you want clean design you will end up with additional layer of data transfer objects which will use DataContract attribute.Premundane
@Ladislav: I disagree completely that it's a "clean" solution to introduce a new layer of objects that serve no purpose but to shuffle data. There's a reason that Code First (and Code Only) was so strongly demanded by the EF user community...Wollongong
C
7

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.

Cinder answered 15/2, 2011 at 22:54 Comment(2)
Thanks Ladislav and ChristopherousLevison
Similar answer here: jameskovacs.com/2006/11/18/going-around-in-circles-with-wcfNostomania
F
0

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;}
}
Finbur answered 20/10, 2016 at 7:32 Comment(0)
K
0

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;
    }      
Kanaka answered 16/3, 2018 at 20:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.