Circular References and WCF

后端 未结 3 1057
挽巷
挽巷 2021-01-12 21:21

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

相关标签:
3条回答
  • 2021-01-12 21:25

    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;
        }      
    
    0 讨论(0)
  • 2021-01-12 21:44

    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;}
    }
    
    0 讨论(0)
  • 2021-01-12 21:47

    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.

    0 讨论(0)
提交回复
热议问题