Циркулярные ссылки и WCF - PullRequest
       34

Циркулярные ссылки и WCF

6 голосов
/ 11 февраля 2011

Я сгенерировал свои объекты POCO с помощью генератора POCO, в моей базе данных более 150+ таблиц.Я разделяю объекты POCO на всех уровнях приложений, включая клиента.Я отключил LazyLoading и ProxyCreation в моем контексте. Я использую WCF поверх моего доступа к данным и бизнес-уровня.

Теперь, когда я возвращаю объект poco своему клиенту, я получаю сообщение об ошибке " Базовое соединение было закрыто " Я включил трассировку WCF и обнаружил точную ошибку: Содержит циклы и не может быть сериализован, если отслеживание ссылок отключено .

Я посмотрел на MSDN и нашел решения, такие как установка IsReference = true в атрибуте метода DataContract, но я не украшаю свои классы POCO с помощью DataContracts, и я предполагаю, что в этом также нет необходимости.Я не буду называть это POCO, если я украслю класс атрибутом DataContract

Затем я нашел решения, такие как применение пользовательского атрибута [CyclicReferenceAware] поверх моего ServiceContracts. Это сработало, но я хотел бросить этот вопроссообществу, чтобы узнать, как это удалось другим, а также почему Microsoft не предоставила встроенную поддержку для определения циклических ссылок при сериализации классов POCO

Ответы [ 3 ]

7 голосов
/ 16 февраля 2011

Вы уже упомянули подход, но я использую этот атрибут

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
}

} ... и ссылка на операцию в Сервисе, как это;

[OperationContract]
[ReferencePreservingDataContractFormat]
IList<SomeObject> Search(string searchString);

К вашему сведению - хотел бы отдать должное, где это необходимо, но не записал, где я первоначально увидел вышеуказанный подход.

Edit:

Я считаю, что источником кода является сообщение в блоге .

0 голосов
/ 16 марта 2018

Я использую следующие вызовы для выравнивания сущностей EntityFramwork POCO перед возвратом из службы WCF.Он обрежет круговой дочерний объект на основе значения maxLevel.

    /// <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 голосов
/ 20 октября 2016

У меня была та же проблема, и я решил ее, исключив свойство навигации обратно к родителю из 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;}
}
...