Невозможно преобразовать базовый класс (контракт данных) в производный класс

[DataContract]
public class SearchCriteria
{
    [DataMember]
    public string CountryID { get; set; }    
}

[DataContract]
public class CitySearchCriteria: SearchCriteria
{
    [DataMember]
    public string CityID { get; set; }
}

Я создаю экземпляр SearchCriteria в своем действии контроллера MVC и пытаюсь преобразовать его в CitySearchCriteria.

SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.CountryID = "1";
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

Объект «citySearchCriteria» после приведенного выше оператора показывает значение NULL. Я ожидал, что он покажет оба свойства: CountryID и CityID с заполненным CountryID и пустым CityID... но он устанавливает для объекта значение NULL.

Какое здесь может быть решение? Имеет ли DataContract какое-либо отношение к этому?

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

CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;

Это успешно конвертируется, так почему аналогичная вещь не работает в действии контроллера?


person Nirman    schedule 06.05.2013    source источник
comment
Вы не можете разыграть из Базового в Производное. Обратное возможно   -  person noobob    schedule 06.05.2013
comment
пожалуйста, смотрите последние два абзаца в вопросе   -  person Nirman    schedule 06.05.2013
comment
Проверьте еще раз ответ Мартина (1-й пример).   -  person noobob    schedule 06.05.2013


Ответы (3)


Все уже (и правильно) сказали вам, что вы просто не можете выполнить приведение из Base к Derived, но мне кажется, что вы все еще не понимаете, почему эта строка работает в другом фрагменте вашего кода:

CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;

Я думаю, что вы немного запутались в том, что такое «Тип» экземпляра. Вы не опубликовали определение модели, но я думаю, что у вас есть что-то вроде этого:

public SearchCriteria SearchCriteria;

Это не означает, что SearchCriteria всегда содержит экземпляры SearchCriteria, а только то, что он содержит экземпляры типов, которые можно привести к SearchCriteria. В вашем случае он может содержать экземпляры SearchCriteria или CitySearchCriteria. Я полагаю, что где-то в вашем коде вы найдете что-то вроде:

Model.SearchCriteria = new CitySearchCriteria();

и это то, что позволяет вашему броску быть выполненным правильно. Вы можете видеть, что экземпляр действительно является CitySearchCriteria (а не просто экземпляром SearchCriteria), выполняющим этот код непосредственно перед приведением:

MessageBox.Show(Model.SearchCriteria.GetType().FullName);

Чтобы лучше понять, вы можете попробовать изменить значение в SearchCriteria непосредственно перед вашим рабочим приведением, как показано ниже, только чтобы узнать, что приведение больше не будет работать:

Model.SearchCriteria = new SearchCriteria();
MessageBox.Show(Model.SearchCriteria.GetType().FullName);
CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;
person Francesco Baruchelli    schedule 06.05.2013
comment
спасибо, да, вы правы, у меня было много путаницы по этому поводу. эти детали прояснили то же самое, и я мог видеть, где я ошибаюсь в кодировании этого. Спасибо - person Nirman; 06.05.2013

Вы не можете бросать таким образом!

Если вы делаете new, вы создаете новый объект памяти определенного размера. В вашем случае new SearchCriteria() создает новый объект памяти достаточного размера для хранения одной строки, ни больше, ни меньше.

В последней строке вы делаете searchCriteria as CitySearchCriteria, пытаясь преобразовать объект в searchCriteria в более крупный тип CitySearchCriteria. Но это невозможно. Вы пытаетесь «преобразовать» объект памяти, который содержит 1 строку, в объект памяти, который может содержать 2 строки. Но приведение не преобразует новый объект памяти. Каково будет значение новой строки? Он просто смотрит под воду, чтобы проверить, содержит ли уже ваша ссылка searchCriteria объект типа CitySearchCriteria. В вашем случае: это не так (объект имеет тип SearchCriteria) и возвращает null.

Итак... следующий пример РАБОТАЕТ (потому что CitySearchCriteria уже создан). Это также ваше решение:

SearchCriteria searchCriteria = new CitySearchCriteria(); 
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

И это не работает (поскольку CitySearchCriteria еще НЕ создана). Это ваша ситуация:

SearchCriteria searchCriteria = new SearchCriteria();
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

Это то же самое, что и в следующем примере.
Это действительно работает (поскольку SearchCriteria уже создан):

object o = new SearchCriteria();
SearchCriteria searchCriteria = o as SearchCriteria;

И это нет (поскольку SearchCriteria еще НЕ был создан)::

object o = new object();
SearchCriteria searchCriteria = o as SearchCriteria;

Для справки: я бы всегда использовал прямое приведение, а не приведение с использованием as, если только вы не хотите явно проверять, относится ли объект к этому типу.

person Martin Mulder    schedule 06.05.2013
comment
Он просто не может быть преобразован, потому что, исходя из его реализации, не будет работать приведение из базового класса в производный. Обратное возможно. - person noobob; 06.05.2013
comment
Я думаю, это именно то, что я сказал... не так ли? Или я не понимаю, что вы имеете в виду? Какой пример в моем тексте неверен? - person Martin Mulder; 06.05.2013
comment
Да, третий будет работать. Объект может содержать ЛЮБОЙ тип. И вы можете привести его к любому типу, пока он поддерживает этот тип. Другой пример: object o = "Hello"; string s = o as string; Почему вы думаете, что это не сработает? - person Martin Mulder; 06.05.2013
comment
Я не это имел в виду. Где решение его вопроса? - person noobob; 06.05.2013
comment
Решение — пример №1. - person Martin Mulder; 06.05.2013
comment
Тогда я возвращаю вам +1 :) - person noobob; 06.05.2013
comment
Спасибо за то, что держишь меня в тонусе и бодрствуешь! :) - person Martin Mulder; 06.05.2013

Вы можете создать CitySearchCriteria и привести его к SearchCriteria. Таким образом, вы можете видеть только CountryId. Позже вы можете вернуть его в CitySearchCriteria и увидеть CountryId и CityId.

Это не имеет ничего общего с DataContract. Решением в вашем случае будет создание CitySearchCriteria и приведение его к SearchCriteria (если вам это нужно).

person uross    schedule 06.05.2013
comment
Я нашел два похожих вопроса: stackoverflow.com/questions/988658/ и stackoverflow.com/questions/9885644/ - person uross; 06.05.2013
comment
Дело в том, что мое представление ожидает объект SearchCriteria, потому что он был вызван из нескольких мест, которые могут иметь CitySearchCriteria, StateSearchCriteria и т. д. в зависимости от вызывающего контроллера и действия. Кроме того, я успешно сделал это в своих представлениях, например, CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria; и это работает нормально, так почему бы не в действии контроллера? - person Nirman; 06.05.2013