How to enable property value conversion in NHibernate QueryOver.SelectList?

I want to include translations of property values ​​in QueryOver queries.

I like to write queries following the pattern of the query object, directly creating MVC view models. In my opinion, I try to use models as simple as possible types of properties, while preserving the complexity of the conversion from views and controllers. This means that sometimes I need to convert one type to another, like dates to strings.

It can be argued that such transformations should be performed in views, but since most of my view models directly go to JSON objects, this will make the conversion much more cumbersome. Running a date to convert strings to JavaScript is problematic at best, and my JSON converter is not flexible enough.

Here is an example of what I am 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 the request code is run, the following exception is thrown:

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

Obviously, this is due to the following line:

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

So the question is: how do I turn the date to string conversion in the query?

I do not want to perform the conversion after the query is completed, because I need an additional intermediate class to store the results before converting them.

+6
source share
2 answers
 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 applied a similar problem by specifying a private property of the model that contains the data as the correct type and property of the string to return a formatted value, i.e.

 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 may not work well with your JSON converter. Does your converter have a parameter or attribute that allows it to ignore private properties?

+9
source

Today I ran into the same problem and saw this post. I went ahead and created my own transformer, which can be provided with converter functions to handle type conversions for each property.

Here is the Transformer class.

 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:

 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, when I call my request, it returns the Start and End values ​​as 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.

+3
source

Source: https://habr.com/ru/post/894530/


All Articles