Проблема DataContractResolver / KnownType, когда пользовательский класс содержит другой пользовательский класс - PullRequest
5 голосов
/ 08 июля 2011

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

Type 'Castle.Proxies.JokeCategoryProxy' with data contract name 
'JokeCategoryProxy:http://schemas.datacontract.org/2004/07/Castle.Proxies' 
is not expected. Consider using a DataContractResolver or add any types not 
known statically to the list of known types - for example, by using the
KnownTypeAttribute attribute or by adding them to the list of known 
types passed to DataContractSerializer.

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

[DataContract]
[KnownType(typeof(ModelBase<int>))]
public class Joke : ModelBase<int>
{
    [DataMember]
    public virtual string JokeText { get; set; }

    [DataMember]
    public virtual JokeCategory JokeCategory { get; set; }
}

[DataContract]
[KnownType(typeof(ModelBase<int>))]
public class JokeCategory : ModelBase<int>
{
    [DataMember]
    public virtual string Name { get; set; }
}

Как вы можете видеть, модель Joke содержит объект категории Joke, если я удаляю категорию Joke и просто имею int (JokeCategoryId) ошибка исчезает, хотя решение, не идеальное, поскольку я хотел бы, чтобы Категория была доступна без повторного запроса.

Ниже приведен код, который я использую для генерации json

    public static ContentResult JsonResponse<TReturnType>(this Controller controller, TReturnType data)
    {
        using (var oStream = new System.IO.MemoryStream())
        {
            new DataContractJsonSerializer(typeof(TReturnType)).WriteObject(oStream, data);

            return new ContentResult
            {
                ContentType = "application/json",
                Content = Encoding.UTF8.GetString(oStream.ToArray()),
                ContentEncoding = Encoding.UTF8
            };
        }
    }

Что меня больше всего смущает, так это то, что ошибка ссылается на Castle.Proxies.JokeCategoryProxy (откуда это взялось!!)

Есть предложения?

1 Ответ

6 голосов
/ 09 июля 2011

nHibernate предполагает, что все ваши свойства, если не указано иное, загружаются с отложенной загрузкой.
Это означает, что в вашем случае JokeCategory не извлекается из БД всякий раз, когда вы извлекаете Jokeобъект;вместо этого динамически генерируется «прокси».
При первом обращении к этому свойству nHibernate знает, как извлечь его из БД.(так работает ленивая загрузка nHib)

Итак, в основном здесь происходит то, что вы ожидаете, что ваш JokeCategory будет иметь тип JokeCategory, но поскольку он не инициализирован - он имеет типProxy....

(это только краткое объяснение; Google еще немного о nHib и о том, как он работает, чтобы узнать больше. Вы также можете проверить summer of nhibernate для отличного введения в этот ORM)

И, на ваш вопрос: у вас есть несколько вариантов здесь:

  1. настроить ваше свойство Category как ленивое, что заставит nHibernate инициализировать его справильный тип объекта

  2. (на мой взгляд, гораздо предпочтительнее) не сериализуют объекты модели;вместо этого - создайте некоторый тип DTO, который будет содержать любую информацию, которая нужна вашему уровню презентации.
    Таким образом, на вашу презентацию не будут влиять изменения модели предметной области, и наоборот.
    также вы можете хранить все необходимоеинформация в вашем DTO, даже если она связана с более чем одним объектом Model.

Например:

  public class JokeDTO
    {
       public int JokeId;
       /*more Joke properties*/
       public int JokeCategoryId;
       public string JokeCategoryName;
       /*etc, etc..*/
    }
...