如何将属性值转换合并到NHibernate QueryOver.SelectList中

本文关键字:NHibernate QueryOver SelectList 合并 转换 属性 | 更新日期: 2023-09-27 18:04:52

我希望将属性值转换合并到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))

所以问题是:如何将日期到字符串的转换合并到查询中?

我不想在查询执行后执行转换,因为在转换结果之前,我需要一个额外的中间类来存储结果。

如何将属性值转换合并到NHibernate QueryOver.SelectList中

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转换器配合使用。您的转换器是否具有允许其忽略私有属性的设置或属性?

我今天遇到了同样的问题,并看到了这篇帖子。我继续创建了自己的转换器,可以为其提供Converter函数来处理每个属性的类型转换。

这是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"));

希望这能有所帮助。