Я хочу передать список int (List) в качестве декларативного свойства пользовательскому веб-элементу управления следующим образом:
<UC:MyControl runat = "server" ModuleIds = "1,2,3" />
Для этого я создал TypeConverter:
public class IntListConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(
System.ComponentModel.ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string)) return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(
System.ComponentModel.ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(
new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<int> list = new List<int>();
foreach (string s in vals)
{
list.Add(Convert.ToInt32(s));
}
return list
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor)) return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor) && value is List<int>)
{
List<int> list = (List<int>)value;
ConstructorInfo construcor = typeof(List<int>).GetConstructor(new Type[] { typeof(IEnumerable<int>) });
InstanceDescriptor id = new InstanceDescriptor(construcor, new object[] { list.ToArray() });
return id;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
А затем добавил атрибут к моему свойству:
[TypeConverter(typeof(IntListConverter))]
public List<int> ModuleIds
{
get { ... }; set { ... };
}
Но я получаю эту ошибку во время выполнения:
Unable to generate code for a value of type 'System.Collections.Generic.List'1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. This error occurred while trying to generate the property value for ModuleIds.
Мой вопрос аналогичен найденному здесь, но решение не решает мою проблему:
Обновлять: Я нашел страницу, которая решила первую проблему. Я обновил приведенный выше код, чтобы показать свои исправления. Добавленный код - это методы CanConvertTo и ConvertTo. Теперь у меня другая ошибка:
Object reference not set to an instance of an object.
Эта ошибка, по-видимому, косвенно вызвана чем-то в методе ConvertTo.
Ха, нет ... Я исправлю это.
Спасибо, что задали этот вопрос. Я столкнулся с почти такой же проблемой.





передать список из кода позади ...
aspx:
<UC:MyControl id = "uc" runat = "server" />
код программной части:
List<int> list = new List<int>();
list.add(1);
list.add(2);
list.add(3);
uc.ModuleIds = list;
Это обходной путь, который я использовал, но определенно менее оптимален.
На мой взгляд, это выглядит лучше, чем класс преобразователя типа. Какой из них более читабельный?
Проблема в том, что для этого требуются изменения в коде, что нарушает разделение декларативной и недекларативной частей кода.
Обычно я делаю это так, чтобы свойство обертывало коллекцию ViewState. Ваш способ выглядит лучше, если его можно заставить работать, но это выполнит свою работу:
public IList<int> ModuleIds
{
get
{
string moduleIds = Convert.ToString(ViewState["ModuleIds"])
IList<int> list = new Collection<int>();
foreach(string moduleId in moduleIds.split(","))
{
list.Add(Convert.ToInt32(moduleId));
}
return list;
}
}
Идея интересная, но все же не оптимальная, так как не позволяет декларативной разметке работать.
Я считаю, что проблема в наборе {}. Преобразователь типов хочет преобразовать List<int> обратно в строку, но CanConvertFrom() не работает для List<int>.
Нет, этот случай обрабатывается методом ConvertTo, который для ясности не показан.
Вы можете передать его в строку и разделить запятыми, чтобы заполнить частную переменную. Не имеет тонкости атрибуции, но будет работать.
private List<int> modules;
public string ModuleIds
{
set{
if (!string.IsNullOrEmpty(value))
{
if (modules == null) modules = new List<int>();
var ids = value.Split(new []{','});
if (ids.Length>0)
foreach (var id in ids)
modules.Add((int.Parse(id)));
}
}
Это возможно, но не решает проблемы. То, что я пытаюсь сделать, должно быть возможным, не так ли?
Хотя я не могу сказать, что у меня есть особый опыт работы с этой ошибкой, другие источники указывают, что вам необходимо добавить преобразование в тип InstanceDescriptor. проверить:
http://weblogs.asp.net/bleroy/archive/2005/04/28/405013.aspx
Что дает объяснение причин или альтернативно:
http://forums.asp.net/p/1191839/2052438.aspx#2052438
Который предоставляет пример кода, похожего на ваш.
Кажется, это правильное направление, но я все равно получаю сообщение об ошибке. Смотрите мое обновление.
Я думаю, что лучший вариант - сделать так, чтобы ваш пользовательский элемент управления имел свойство в стиле DataSource.
Вы берете свойство как объект, а затем выполняете некоторую проверку типов по IList / IEnumerable / etc, чтобы убедиться, что оно правильное.
После подключения отладчика к Cassini я вижу, что нулевой ref на самом деле исходит от System.Web.Compilation.CodeDomUtility.GenerateExpressionForValue, который в основном пытается получить выражение для массива int [], который вы передаете в конструктор List. Поскольку для массива int [] нет дескриптора типа, он терпит неудачу (и выдает в процессе пустую ссылку вместо «не может сгенерировать исключение набора свойств», которое должно).
Я не могу понять встроенный способ получения сериализуемого значения в List <int>, поэтому я просто использовал статический метод:
class IntListConverter : TypeConverter {
public static List<int> FromString(string value) {
return new List<int>(
value
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Convert.ToInt32(s))
);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(InstanceDescriptor)) {
List<int> list = (List<int>)value;
return new InstanceDescriptor(this.GetType().GetMethod("FromString"),
new object[] { string.Join(",", list.Select(i => i.ToString()).ToArray()) }
);
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
+1 И за этот ответ. Рабочее исправление очень малоизвестной проблемы. У меня была та же проблема, что и у OP, за исключением использования List<string> вместо List<int>. Решение отлично сработало с несколькими незначительными изменениями для учета строки и int.
Спасибо за этот ответ. Теперь я понимаю: построитель страниц может генерировать выражение «1,2,3», но не может генерировать новое выражение List <string> {«1», «2», «3»}. Есть ли способ указать строителю выдавать такое выражение?
Мне не понравилась идея записать значение в виде строки типа «1,2,3» в сгенерированном файле класса управления, потому что оно будет десериализоваться при каждом запросе, поэтому я написал ControlBuilder для своего выражения, чтобы затем инициализировать коллекцию добавляет каждое значение, вызывая метод Add
Я решил нечто похожее, создав 2 свойства:
public List<int> ModuleIDs { get .... set ... }
public string ModuleIDstring { get ... set ... }
ModuleIDstring преобразует свой набор значений в список и устанавливает свойство ModuleIDs.
Это также позволит использовать ModuleID из PropertyGrid и т. д.
Хорошо, не лучшее, безопасное решение, но для меня оно работает.
Вероятно, это решение, которое мы в конечном итоге выберем, но все еще странно, что мы не можем понять, как исправить настоящую ошибку.
Конечно, вы не написали IntListConverter в имени класса и IntegerListConverter в атрибуте, не так ли?