Динамическая сортировка с помощью SerializableDynamicObject
Мне нужно сортировать коллекцию из них на основе критериев, определенных во время выполнения.
Я использовал код из этой статьи для выполнения сортировки - изначально мой код использовал динамический класс.
Затем я ударил проблемы с сериализацией по WCF, поэтому я переключился на SerializableDynamicObject, и теперь код сортировки разбивается на строку:
PropertyInfo pi = type.GetProperty(prop);
с ошибкой, что SerializableDynamicObject не имеет свойства под названием "Имя", где "Имя" было значением prop.
Я думаю, что самое простое - найти альтернативный способ сериализации динамического типа, с которым работает алгоритм сортировки. Любые указатели в этом направлении будут оценены!
Я рассмотрел этот пример, но я получил сообщение об ошибке:
The constructor with parameters (SerializationInfo, StreamingContext) is not found in ISerializable type
Ответы
Ответ 1
Здесь используется код FastMember, который работает как для объектов на основе отражения, так и для dynamic
(в зависимости от того, что вы переходите к TypeAccessor.Create
)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using FastMember;
namespace ConsoleApplication6
{
class Program
{
static void Main()
{
var list = new List<dynamic>();
dynamic obj = new ExpandoObject();
obj.Foo = 123;
obj.Bar = "xyz";
list.Add(obj);
obj = new ExpandoObject();
obj.Foo = 456;
obj.Bar = "def";
list.Add(obj);
obj = new ExpandoObject();
obj.Foo = 789;
obj.Bar = "abc";
list.Add(obj);
var accessor = TypeAccessor.Create(
typeof(IDynamicMetaObjectProvider));
string propName = "Bar";
list.Sort((x,y) => Comparer.Default.Compare(
accessor[x, propName], accessor[y,propName]));
foreach(var item in list) {
Console.WriteLine(item.Bar);
}
}
}
}
Возможно, стоит обратить внимание на то, что для типов, основанных на отражении, это означает не использование рефлексии по каждому элементу; все, что оптимизировано с помощью метапрограммирования.
Ответ 2
Ответ Марка Гравелла дал мне ответ, чтобы закончить это - мне нужно было создать сортировщик, который мог бы обрабатывать несколько критериев сортировки, неизвестных до выполнения. Я принимаю ответ Марка, но публикую его, поскольку кто-то может также найти его полезным.
Там может быть более элегантный способ достижения этого, если да, пожалуйста, дайте мне знать, и я обновлю ответ.
public class SerializableDynamicObjectComparer: IComparer
{
private readonly List<KeyValuePair<string, bool>> sortCriteria = new List<KeyValuePair<string, bool>>();
private readonly TypeAccessor accessor;
public SerializableDynamicObjectComparer(IEnumerable<string> criteria)
{
foreach (var criterium in criteria)
{
string[] sortCriterium = criterium.Split('.');
this.sortCriteria.Add(new KeyValuePair<string, bool>(sortCriterium[0],
sortCriterium.Length == 0
? sortCriterium[1].ToUpper() == "ASC"
: false));
}
this.accessor = TypeAccessor.Create(typeof (IDynamicMetaObjectProvider));
}
public int Compare(object x, object y)
{
for(int i=0; i< this.sortCriteria.Count; i++)
{
string fieldName = this.sortCriteria[i].Key;
bool isAscending = this.sortCriteria[i].Value;
int result = Comparer.Default.Compare(this.accessor[x, fieldName], this.accessor[y, fieldName]);
if(result != 0)
{
//If we are sorting DESC, then return the -ve of the default Compare result
return isAscending ? result : -result;
}
}
//if we get here, then objects are equal on all sort criteria.
return 0;
}
}
Использование:
var sorter = new SerializableDynamicObjectComparer(sortCriteria);
var sortableData = reportData.ToList();
sortableData.Sort(sorter.Compare);
где sortCriteria - это массив строк, например.
new {"Name.DESC", "Age.ASC", "Count"}