Как использовать MemoryCache для ускорения перевода различных объектов в строки?

У меня есть большой набор данных (IEnumerable из [Table]-атрибутированных объектов класса) из запроса Linq-To-Sql, и мне нужно создать из него файл CSV. Я перебираю набор данных и для каждого элемента преобразую значение каждого свойства элемента в строку, используя различные параметры форматирования.

Type t = typeof(T);
var properties = t.GetProperties();

foreach (var item in list)
{
    foreach (var property in properties)
    {
        // This is made using delegates, but whatever
        object value = property.GetValue(item, null);
        // convert to string and feed to StringBuilder
    }
}

Проблема в том, что преобразование занимает даже больше времени, чем выполнение запроса. Набор данных содержит сильно денормализованные данные — многие элементы имеют одни и те же свойства, имеющие одинаковые значения, и только некоторые свойства имеют разные значения. Каждое значение свойства переводится отдельно для каждого элемента в наборе данных. Поэтому мой код преобразует одни и те же данные в одни и те же строки — снова и снова. И хотелось бы как-то это ускорить, желательно без изменения SQL-запроса.

Похоже на MemoryCache class< /a> может работать, но мне нужно создать уникальные ключи для каждого объекта. Я не могу понять, как я могу создать такие ключи надежно и достаточно эффективно.

Как использовать MemoryCache, чтобы кэшировать результаты перевода для объектов разных типов?


person sharptooth    schedule 18.04.2014    source источник


Ответы (1)


Если вы просто хотите ускорить его, я бы предложил ExpressionTrees больше, чем MemoryCache. Это предполагает, что у вас нет вложенных объектов, которые вы хотите прочитать, и я могу использовать отражение для первого элемента, и оно будет одинаковым для каждого элемента в IEnumerable, что из вашего примера кода в вопросе кажется правильным.

Кроме того, если он большой и вы собираетесь просто записать его в файл, я бы предложил перейти прямо к FileStream вместо StringBuilder.

public class CSV
{
    public static StringBuilder ToCSV(IEnumerable list)
    {
        Func<object, object[]> toArray = null;
        var sb = new StringBuilder();

        // Need to initialize the loop and on the first one grab the properties to setup the columns
        foreach (var item in list)
        {
            if (toArray == null)
            {
                toArray = ItemToArray(item.GetType());
            }
            sb.AppendLine(String.Join(",", toArray(item)));
        }

        return sb;
    }

    private static Func<object, object[]> ItemToArray(Type type)
    {
        var props = type.GetProperties().Where(p => p.CanRead);
        var arrayBody = new List<Expression>();

        // Create a parameter to take the item enumeration
        var sourceObject = Expression.Parameter(typeof (object), "source");
        // Convert it to the type that is passed in
        var sourceParam = Expression.Convert(sourceObject, type);

        foreach (var prop in props)
        {
            var propType = prop.PropertyType;

            if (IsValueProperty(propType))
            {
                // get the value of the property
                Expression currentProp = Expression.Property(sourceParam, prop);
                // Need to box to an object if value type
                if (propType.IsValueType)
                {
                    currentProp = Expression.TypeAs(currentProp, typeof (object));
                }
                // Add to the collection of expressions so we can build the array off of this collection
                arrayBody.Add(currentProp);
            }
        }
        // Create an array based on the properties
        var arrayExp = Expression.NewArrayInit(typeof (object), arrayBody);

        // set a default return value of null if couldn't match
        var defaultValue = Expression.NewArrayInit(typeof (object), Expression.Constant(null));

        //Set up so the lambda can have a return value
        var returnTarget = Expression.Label(typeof (object[]));
        var returnExpress = Expression.Return(returnTarget, arrayExp, typeof (object[]));
        var returnLabel = Expression.Label(returnTarget, defaultValue);

        //Create the method
        var code = Expression.Block(arrayExp, returnExpress, returnLabel);
        return Expression.Lambda<Func<object, object[]>>(code, sourceObject).Compile();
    }


    private static bool IsValueProperty(Type propertyType)
    {
        var propType = propertyType;

        if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof (Nullable<>))
        {
            propType = new NullableConverter(propType).UnderlyingType;
        }

        return propType.IsValueType || propType == typeof (string);
    }
}
person CharlesNRice    schedule 18.04.2014