Странная проблема с отражением - не могу ее получить - PullRequest
3 голосов
/ 12 января 2011

, пожалуйста, помогите мне понять, что здесь происходит и должно ли это работать так? У меня есть общий список объектов из CMS:

Например List<MyCMS.Articles.Article> myArticles = articles.All;

Позже я вывожу содержимое списка в формате JSON (для CMS UI - список таблиц).

Теперь одна запись будет включать:

article.Title
article.Alias
article.Guid
article.Description
+
article.SeoProperties.TitleOverride
article.SeoProperties.H1Tag
article.StateProperties.IsActive
article.StateProperties.Channels

и т.д ...

как вы можете видеть, у объекта Article есть дополнительное свойство класса - с общими свойствами (используется для других типов объектов в CMS)

Я также использую класс фильтра, который выполняет некоторые операции фильтрации с LINQ для коллекции, чтобы возвращать мне только статьи в определенном канале, например ...

Так что проблема в том, что когда я сериализирую коллекцию как JSON - в списке таблиц мне действительно нужно отобразить только несколько «столбцов», в то время как мне не нужны другие поля, особенно длинные поля, например как "описание" (ленивый загруженный из файловой системы) и т. д. - я сериализую с DataContractJsonSerializer ...

Мне нужен способ контролировать, какие поля будут включены в результат JSON ... Что я делаю, так это использую отражение, чтобы установить значения свойств в null, если мне не нужно это свойство, и украсить свойства класса с помощью атрибутов [DataMember (IsRequired = false, EmitDefaultValue = false)] ... - это должно работать хорошо - но - как только я перебираю (даже клонирую !!) коллекцию конечных объектов для удаления полей = установите значение в «null» - значение свойства становится равным null - для всего приложения - во всех коллекциях таких объектов ... а?

Демонстрационный код здесь:

void Page_Load() {
        MyCms.Content.Games games = new MyCms.Content.Games();
        List<MyCms.Content.Games.Game> allGames = games.All;

        MyCms.Content.Games games2 = new MyCms.Content.Games();
        List<MyCms.Content.Games.Game> allGamesOther = games2.All;

        Response.Write("Total games: " + allGames.Count + "<br />");

        //This is our fields stripper - with result assigned to a new list
        List<MyCms.Content.Games.Game> completelyUnrelatedOtherIsolated_but_notSureList = RemoveUnusedFields(allGamesOther);

        List<MyCms.Content.Games.Game> gamesFiltered = allGames.Where(g=>g.GamingProperties.Software=="89070ef9-e115-4907-9996-6421e6013993").ToList();

        Response.Write("Filtered games: " + gamesFiltered.Count + "<br /><br />");

    }

    private List<MyCms.Content.Games.Game> RemoveUnusedFields(List<MyCms.Content.Games.Game> games)
    {
        List<MyCms.Content.Games.Game> result = new List<MyCms.Content.Games.Game>();

        if (games != null && games.Count > 0)
        {
            //Retrieve a list of current object properties
            List<string> myContentProperties = MyCms.Utils.GetContentProperties(games[0]);

            MyCms.PropertyReflector pF = new MyCms.PropertyReflector();

            foreach (MyCms.Content.Games.Game contentItem in games)
            {
                MyCms.Content.Games.Game myNewGame = (MyCms.Content.Games.Game)contentItem.Clone();
                myNewGame.Images = "wtf!"; //just to be sure we do set this stuff not only null

                pF.SetValue(myNewGame, "GamingProperties.Software", ""); //set one property to null for testing

                result.Add(myNewGame);

            }
        }

    return result;
}

Объектам присваиваются свои «Значения по умолчанию» (в основном, ноль, в большинстве случаев) с этим:

 private object GetDefaultValue(Type type)
        {
            if (type.IsValueType)
            {
                try
                {
                    return Activator.CreateInstance(type);
                }
                catch {
                    return null;
                }
            }

            return null;
        }

Ответы [ 2 ]

2 голосов
/ 12 января 2011

Вполне возможно, у вас проблемы с различием между мелкой копией и глубокой копией.

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

С глубокой копией, когда вы клонируете объект, и у этого объекта есть поле ссылочного типа, новый клон этого объекта создается и присваивается полю (вместо простой ссылки на первый объект). Таким образом, вы должны совершенно разные объекты, которые не имеют ничего общего.

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

У вас есть больше информации об этом в

http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

0 голосов
/ 12 января 2011

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

...