#c# #.net #dynamic #casting #valueinjecter
#c# #.net #динамический #Кастинг #valueinjector
Вопрос:
Я создал пользовательский класс внедрения для ValueInjecter, который выполняет рекурсивное внедрение для общих дочерних коллекций, но который работает с существующими целевыми объектами, а не клонирует, как пример «CloneInjection» на сайте VI. Однако в настоящее время я выполняю приведение к известному типу (ICollection<MyTargetType>), поэтому класс внедрения подходит только для одного жестко запрограммированного типа. Кажется, я не могу придумать способ динамического приведения общего, есть предложения? Вот код setValue для моего класса «RecursiveInjection».
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string))
return c.SourceProp.Value;
if (c.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var t = c.TargetProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;
//get enumerable object in target
var targetCollection = c.TargetProp.Value as ICollection<MyTargetType>;
//possible to cast dynamically?
foreach (var o in c.SourceProp.Value as IEnumerable)
{
//get ID of source object
var sourceID = (int)o.GetProps().GetByName("ID").GetValue(o);
//find matching target object if there is one
var target = targetCollection.SingleOrDefault(x => x.ID == sourceID);
if (target != null)
{
target.InjectFrom<RecursiveInjection>(o);
}
}
return targetCollection;
}
}
Спасибо, Дано
Комментарии:
1. не могли бы вы рассказать мне, чего вы пытаетесь достичь?
2. @Omu — взгляните на мои сообщения на странице VI «Глубокое клонирование» — valueinjecter.codeplex.com / … — Я обновляю объекты EF POCO, поэтому мне нужно напрямую обновлять свойства целевого объекта, а не создавать клоны
3. Я понял, что вы пытаетесь сделать что-то вроде внедрения clone, но без клонирования, а также что вам это удалось с помощью <T>, но вам нужно более динамичное (вероятно, вы также могли бы попробовать выполнить универсальное внедрение), я хотел бы знать, почему / что вы пытаетесь сделать, каковы ваши исходные и целевые объекты и что вы хотите внедрить из одного в другой
4. @Omu — Я пытаюсь создать повторно используемую инъекцию, которая будет вводить объекты POCO, загруженные из контекста EF, со значениями из аналогичных объектов viewmodel, размещенных через форму MVC, и я хочу, чтобы инъекция рекурсивно обрабатывала все дочерние коллекции и их свойства (например, объект Customer, у которого есть дочерняя коллекция заказов). Клиент. Заказы = CustomerViewModel. Orders не работает с EF, вот почему внедрение clone не работает. Имеет смысл?
5. это выполнимо, но есть проблема с коллекциями, если, например, на стороне исходного кода был удален элемент из середины коллекции, как бы вы узнали, из какого объекта из исходной коллекции в какой объект целевой коллекции нужно внедрить, вы не можете просто взять их один за другим, думая, что они находятся в одном порядке, потому что они могут быть не в том порядке, в котором они находятся.
Ответ №1:
Я бы просто использовал ICollection и переопределил Equals (object ) для ваших сравнений.
public class MyTargetType
{
public override bool Equals( object obj )
{
return ( obj is MyTargetType )
? this.ID == ( ( MyTargetType ) obj ).ID
: false;
}
}
Тогда в вашем цикле вы бы сопоставляли объект напрямую, вместо использования отражения.
var target = targetCollection.SingleOrDefault( x => x.Equals( o ) );
Комментарии:
1. спасибо за предложение — я думаю, это решит только часть проблемы. Если я просто приведу targetCollection как ICollection, SingleOrDefault не является допустимым методом для объекта
Ответ №2:
Хотя я не смог придумать способ динамического приведения общей коллекции, я обнаружил решение основной проблемы, которую я пытался решить, которая заключалась в внедрении объектов EF POCO, содержащих дочерние коллекции («свойства навигации» «один ко многим»).
Я только что обновился с EF 4.0 до EF 4.1, и аллилуйя! EF 4.1 теперь фактически управляет слиянием объектов в дочерних коллекциях. После обновления я могу использовать CloneInjection как есть.
Комментарии:
1. так вам это больше не нужно 🙂 ?
2. @Omu — похоже, это проблема, которую мне больше не нужно решать, хотя это все равно было бы интересным упражнением. Вы правы в том, что опубликованный мной код обрабатывает только индивидуальные обновления. В конечном итоге я надеялся выработать логику для обработки добавленных и удаленных элементов.
3. ну, вы можете, но вы должны сделать это не для любого объекта, а для IEntity, который будет иметь свойство Id, таким образом, вы можете сравнивать объекты по Id
Ответ №3:
Я думаю, это то, что вы ищете:
public class M1
{
public string Name { get; set; }
}
public class M2
{
public string Name { get; set; }
}
[Test]
public void Cast()
{
object source = new List<M1> {new M1 {Name = "o"}};
object target = new List<M2>();
var targetArgumentType = target.GetType().GetGenericArguments()[0];
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(targetArgumentType));
var add = list.GetType().GetMethod("Add");
foreach (var o in source as IEnumerable)
{
var t = Activator.CreateInstance(targetArgumentType);
add.Invoke(list, new[] { t.InjectFrom(o) });
}
target = list;
Assert.AreEqual("o", (target as List<M2>).First().Name);
}
с .NET 4 вы можете сделать это немного короче, используя dynamic