简体   繁体   English

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

[英]How to incorporate property value conversion into NHibernate QueryOver .SelectList?

I'm looking to incorporate property value translations into my QueryOver queries. 我希望将属性值转换合并到我的QueryOver查询中。

I like writing queries following the Query Object Pattern, directly producing MVC view models. 我喜欢在查询对象模式之后编写查询,直接生成MVC视图模型。 In my view models, I try to use property types that are as simple as possible, keeping conversion complexity out of the views and controllers. 在我的视图模型中,我尝试使用尽可能简单的属性类型,将转换复杂性保留在视图和控制器之外。 This means that sometimes, I'll need to convert one type into another, such as dates into strings. 这意味着有时候,我需要将一种类型转换为另一种类型,例如将日期转换为字符串。

One could argue that such conversions should be performed in views but since most of my view models are directly translated to JSON objects, that would cause the conversion to become much more cumbersome. 有人可能会争辩说,这种转换应该在视图中执行,但由于我的大多数视图模型都直接转换为JSON对象,这会导致转换变得更加麻烦。 Performing date to string conversion in JavaScript is problematic at best and my JSON convertor is not flexible enough. 在JavaScript中执行日期到字符串转换充其量是有问题的,我的JSON转换器不够灵活。

Here's an example of what I'm doing: 这是我正在做的一个例子:

// 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();

When running the query code, the following exception is thrown: 运行查询代码时,抛出以下异常:

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

Obviously, this is because of the following line: 显然,这是因为以下几行:

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

So the question is: how to incorporate the date to string conversion into the query? 所以问题是:如何将日期转换为字符串转换为查询?

I don't want to perform the conversion after the query has executed because I would need an additional intermediate class to store the results before converting them. 我不想在执行查询后执行转换,因为在转换它们之前我需要一个额外的中间类来存储结果。

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();

Should work but unfortunately does not give you any control over the format of the string. 应该工作但不幸的是没有给你任何控制字符串的格式。 I've handled a similar problem by defining a private property on the model that holds the data as the correct type and a string property to return the formatted value, ie: 我通过在模型上定义一个私有属性来处理类似的问题,该属性将数据保存为正确的类型,并使用字符串属性来返回格式化的值,即:

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

This has several advantages but might not work well with your JSON converter. 这有几个优点,但可能不适合您的JSON转换器。 Does your converter have a setting or attribute that would allow it to ignore private properties? 您的转换器是否具有允许其忽略私有属性的设置或属性?

I came across the same problem today, and saw this posting. 我今天遇到了同样的问题,看到了这个帖子。 I went ahead and created my own transformer that can be given Converter functions to handle type conversions per property. 我继续创建了自己的变换器,可以使用Converter函数来处理每个属性的类型转换。

Here is the Transformer class. 这是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;
    }

And here is how I use it, first my DTO: 以下是我如何使用它,首先是我的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; } 
}

Later on when I call my query, it returns Start and End as DateTime values. 稍后当我调用我的查询时,它将Start和End作为DateTime值返回。 So this is how I actually use the converter. 这就是我实际使用转换器的方式。

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

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM