Как включить преобразование значения свойства в NHibernate QueryOver .SelectList? - PullRequest
7 голосов
/ 08 августа 2011

Я хочу включить переводы значений свойств в мои запросы QueryOver.

Мне нравится писать запросы в соответствии с шаблоном объекта запроса, непосредственно создавая модели представления MVC. В моих моделях представлений я стараюсь использовать максимально простые типы свойств, сохраняя сложность преобразования вне представлений и контроллеров. Это означает, что иногда мне нужно будет преобразовывать один тип в другой, например, даты в строки.

Можно утверждать, что такие преобразования должны выполняться в представлениях, но, поскольку большинство моих моделей представления непосредственно переводятся в объекты JSON, это может привести к тому, что преобразование станет намного более громоздким. Выполнение преобразования даты в строку в JavaScript в лучшем случае проблематично, и мой преобразователь JSON недостаточно гибок.

Вот пример того, что я делаю:

// Entity.
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTimeOffset DateCreated { get; set; }
}

// View model.
public class CustomerViewModel
{
    public string Name { get; set; }
    public string DateCreated { get; set; } // Note the string type here.
}

// Query.
CustomerViewModel model = null;

List<CustomerViewModel> result = Session.QueryOver<Customer>()
    .SelectList(list => list
        .Select(n => n.Name).WithAlias(() => model.Name)
        .Select(n => n.DateCreated).WithAlias(() => model.DateCreated))
    .TransformUsing(Transformers.AliasToBean<CustomerViewModel>());
    .Future<CustomerViewModel>()
    .ToList();

При выполнении кода запроса выдается следующее исключение:

Object of type 'System.DateTimeOffset' cannot be converted to type 'System.String'.

Очевидно, это из-за следующей строки:

.Select(n => n.DateCreated).WithAlias(() => model.DateCreated))

Итак, вопрос: как включить преобразование даты в строку в запрос?

Я не хочу выполнять преобразование после выполнения запроса, потому что мне нужен дополнительный промежуточный класс для хранения результатов перед их преобразованием.

Ответы [ 2 ]

10 голосов
/ 08 августа 2011
List<CustomerViewModel> result = Session.QueryOver<Customer>(() => customerAlias)
    .SelectList(list => list
        .Select(n => customerAlias.Name).WithAlias(() => model.Name)
        // I'm not sure if customerAlias works here or why you have declared it at all
        .Select(Projections.Cast(NHibernateUtil.String, Projections.Property<Customer>(c => c.DateCreated))).WithAlias(() => model.DateCreated))
    .TransformUsing(Transformers.AliasToBean<CustomerViewModel>());
    .Future<CustomerViewModel>()
    .ToList();

Должно работать, но, к сожалению, не дает никакого контроля над форматом строки.Я справился с подобной проблемой, определив частное свойство в модели, в которой данные хранятся как правильный тип, и строковое свойство для возврата отформатированного значения, например:

public class CustomerViewModel
{
    public string Name { get; set; }
    private DateTime DateCreatedImpl { get; set; }
    public string DateCreated { get { return DateCreatedImpl.ToString(); }}
}

. Это имеет несколько преимуществ, но можетне работает с вашим конвертером JSON.Есть ли в вашем конвертере параметр или атрибут, которые позволили бы ему игнорировать частные свойства?

4 голосов
/ 02 декабря 2011

Сегодня я столкнулся с той же проблемой и увидел эту публикацию.Я пошел дальше и создал свой собственный преобразователь, которому могут быть предоставлены функции преобразователя для обработки преобразований типов по каждому свойству.

Вот класс Transformer.

public class AliasToDTOTransformer<D> : IResultTransformer where D: class, new()
{
    //Keep a dictionary of converts from Source -> Dest types...
    private readonly IDictionary<Tuple<Type, Type>, Func<object, object>> _converters;

    public AliasToDTOTransformer()
    {
        _converters = _converters = new Dictionary<Tuple<Type, Type>, Func<object, object>>();
    }

    public void AddConverter<S,R>(Func<S,R> converter)
    {
         _converters[new Tuple<Type, Type>(typeof (S), typeof (R))] = s => (object) converter((S) s);
    }
    public object TransformTuple(object[] tuple, string[] aliases)
    {
        var dto = new D();
        for (var i = 0; i < aliases.Length; i++)
        {
            var propinfo = dto.GetType().GetProperty(aliases[i]);
            if (propinfo == null) continue;
            var valueToSet = ConvertValue(propinfo.PropertyType, tuple[i]);
            propinfo.SetValue(dto, valueToSet, null);
        }
        return dto;
    }
    private object ConvertValue(Type destinationType, object sourceValue)
    {
        //Approximate default(T) here
        if (sourceValue == null)
            return destinationType.IsValueType ? Activator.CreateInstance(destinationType) : null;

        var sourceType = sourceValue.GetType();
        var tuple = new Tuple<Type, Type>(sourceType, destinationType);
        if (_converters.ContainsKey(tuple))
        {
            var func = _converters[tuple];
            return Convert.ChangeType(func.Invoke(sourceValue), destinationType);
        }

        if (destinationType.IsAssignableFrom(sourceType))
            return sourceValue;

        return Convert.ToString(sourceValue); // I dunno... maybe throw an exception here instead?
    }

    public IList TransformList(IList collection)
    {
        return collection;
    }

И вот как я его использую,сначала мой DTO:

public class EventDetailDTO : DescriptionDTO
{
    public string Code { get; set; }
    public string Start { get; set; }
    public string End { get; set; }
    public int Status { get; set; }

    public string Comment { get; set; }
    public int Client { get; set; }
    public int BreakMinutes { get; set; }
    public int CanBeViewedBy { get; set; } 
}

Позже, когда я вызываю свой запрос, он возвращает Start и End как значения DateTime.Вот как я на самом деле использую конвертер.

var transformer = new AliasToDTOTransformer<EventDetailDTO>();
transformer.AddConverter((DateTime d) => d.ToString("g"));

Надеюсь, это поможет.

...