Automapper - Access default map function in MapFrom to fallback - c#

I'm writing an extension method in order to do translation with Automapper.
I have some classes :
public class TranslatableClass : ITranslatable<TranslationClass>
{
public string Id { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public List<TranslationClass> Translations { get; set; }
public string OtherEntityId { get; set; }
public string OtherEntityLabel { get; set; }
public List<OtherEntityTranslation> OtherEntityTranslations { get; set; }
}
public class TranslationClass : ITranslation
{
public Guid LanguageId { get; set; }
public string Label { get; set; }
public string Description { get; set; }
}
public class TranslatedClass
{
public string Id { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public string OtherEntityLabel { get; set; }
}
public class OtherEntityTranslation : ITranslation
{
public string Label { get; set; }
public Guid LanguageId { get; set; }
}
I'd like to get an extension method like this one :
cfg.CreateMap<TranslatableClass, TranslatedClass>()
.ForMember(t => t.OtherEntityLabel, opt => opt.MapFromTranslation(t => t.OtherEntityTranslations, oet => oet.Label));
And my extension method looks like this one
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Func<TSource, IEnumerable<TTranslation>> getTranslations, Func<TTranslation, string> getValue)
where TTranslation : ITranslation
{
opt.MapFrom((src, _, _, context) =>
{
string result = null; // here is the pain point ; I'd like to get the value as if I was automapper
if (context.Options.Items.TryGetValue(LANGUAGE, out object contextLanguage) && contextLanguage is Guid languageId)
{
var translations = getTranslations(src);
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
result = getValue(translation);
}
}
return result;
});
}
The issue I'm facing is I can't find a nice way to get the default behavior of AutoMapper when I don't have a translation. In this implementation, if I don't find a translation for my language, the value will be null while it should be the value of the source object (which is the default value).
I try to put PreCondition before the MapFrom but that doesn't map the property so I get null too.
I can try to get the value from the source object with reflexion but I will lose all the capabilities of Automapper like naming convention and other stuffs.
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Func<TSource, IEnumerable<TTranslation>> getTranslations, Func<TTranslation, string> getValue)
where TTranslation : ITranslation
{
var destinationMember = opt.DestinationMember as PropertyInfo;
var source = typeof(TSource);
var sourceProperty = source.GetProperty(destinationMember.Name);
if (sourceProperty != null)
{
opt.MapFrom((src, _, _, context) =>
{
string result = sourceProperty.GetValue(src) as string; // Get value from source as if it was the mapper
if (context.Options.Items.TryGetValue(LANGUAGE, out object contextLanguage) && contextLanguage is Guid languageId)
{
var translations = getTranslations(src);
if (translations != null)
{
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
var value = getValue(translation);
if (!String.IsNullOrWhiteSpace(value))
{
result = value;
}
}
}
}
return result;
});
}
else
{
throw new Exception($"Can't map property {opt.DestinationMember.Name} from {source.Name}");
}
}

Let's re-define configuration without using extension method, trying to simplify things. Following mapping example, we can implement custom IValueResolver
cfg.CreateMap<TranslatableClass, TranslatedClass>()
.ForMember(dest => dest.OtherEntityLabel, opt => opt.MapFrom<CustomResolver>();
Implementing IValueResolver<TranslatableClass, TranslatedClass, string> interface:
public class CustomResolver: IValueResolver<TranslatableClass, TranslatedClass, string>
{
public string Resolve(TranslatableClass source, TranslatedClass destination, string member, ResolutionContext context)
{
string result = source.Label; /* needed effect! */
/* can we simplify this condition? */
if (context.Options.Items.TryGetValue(source.OtherEntityLabel, out object contextLanguage)
&& contextLanguage is Guid languageId)
{
var translations = source.OtherEntityTranslations;
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
result = translation.Label;
};
}
return result;
}
}
Here comes same logic from
MapFromTranslation<TSource, TDestination, TMember, ... extension method provided below, let's put that logic right - we map TSource as TranslatableClass to TDestination as TranslatedClass.
Also, I believe that if (context.Options.Items.TryGetValue(...)) should be removed for simplicity too (are we trying to get languageId here?)
So, by using Custom Value Resolvers feature we can simplify mapper configuration and refactor for test coverage or debugging needs.
Update
I do want to use this extensions method on 50 others entity and I
won't write custom resolver for each one
Using Expressions instead of reflection should help to implement 'generic solution'. Solution is to define a cache of mapping expressions to access TSource and TDestination properties.
public static class MappingCache<TFirst, TSecond>
{
static MappingCache()
{
var first = Expression.Parameter(typeof(TFirst), "first");
var second = Expression.Parameter(typeof(TSecond), "second");
var secondSetExpression = MappingCache.GetSetExpression(second, first);
var blockExpression = Expression.Block(first, second, secondSetExpression, first);
Map = Expression.Lambda<Func<TFirst, TSecond, TFirst>>(blockExpression, first, second).Compile();
}
public static Func<TFirst, TSecond, TFirst> Map { get; private set; }
}
Next, let's try to define generic lambda-expressions for both
Func<TTranslation, string> getValue and getTranslations(...
e.g.:
public static Expression GetSetExpression(ParameterExpression sourceExpression, params ParameterExpression[] destinationExpressions)
{
/** AutoMapper also can be used here */
/* compile here all (source)=>(destination) expressions */
var destination = destinationExpressions
.Select(parameter => new
{
Parameter = parameter,
Property = parameter.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.FirstOrDefault(property => IsWritable(property) && IsOfType(property, sourceExpression.Type))
})
.FirstOrDefault(parameter => parameter.Property != null);
if (destination == null)
{
throw new InvalidOperationException(string.Format("No writable property of type {0} found in types {1}.", sourceExpression.Type.FullName, string.Join(", ", destinationExpressions.Select(parameter => parameter.Type.FullName))));
}
/* Here is the generic version of mapping code! */
return Expression.IfThen(
Expression.Not(Expression.Equal(destination.Parameter, Expression.Constant(null))),
Expression.Call(destination.Parameter, destination.Property.GetSetMethod(), sourceExpression));
}
Next goes IsWritable(PropertyInfo property) that is used to check validate properties, try to implement convention-based property filtering (names, attributes, etc.) here
public static bool IsWritable(PropertyInfo property)
{
/* eliminating reflection code from extension method */
return property.CanWrite && !property.GetIndexParameters().Any();
}
Next IsOfType(PropertyInfo... and IsSubclassOf methods, - define simple rules of proper TSource->TDestination ways of mapping...
public static bool IsOfType(PropertyInfo property, Type type)
{
/* here AutoMapper could be used too, making filtering needed destination entities by following some convention */
return property.PropertyType == type || IsSubclassOf(type, property.PropertyType) || property.PropertyType.IsAssignableFrom(type);
}
public static bool IsSubclassOf(Type type, Type otherType)
{
return type.IsSubclassOf(otherType);
}
}
Trying to implement convention based mapping approach:
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Expression<Func<TSource, TDestination, TMember, TTranslation>> mapping )
where TTranslation : ITranslation
Wiring around the Expression<Func<TSource,TDestination,TMember, TTranslation> mapping and the MappingCache<TSource,TDestination,TMember, TTranslation>.Map is the next step. Our lambda expression represents the property transformation intent generically (mapping,conversion,validating,navigating, etc...), and when compiled lambda called with parameters passed, we get the result of such transformation.
Expression:
MappingCache<TSource,TDestination,TMember, TTranslation>.GetSetExpression(first, second, third, proxy...
Function:
var result = MappingCache<TSource,TDestination,TMember, TTranslation>.Map(first,second,third,...
Keeping statically compiled lambda-delegates abstractions open, we can cover every needed mapping aspect with proper tests, - seems like the generic approach that could be used to address the question
Access default map function in MapFrom to fallback
(c) Dapper.Mapper + Tests

Related

How to create a generic method to iterate through an object's fields and use it as a Where predicate?

I'm building a generic interface to expose selected string properties out of a class, and then I want to search for a text inside every one of those fields, to check if it's a match.
Here's my IFieldExposer interface:
using System;
using System.Collections.Generic;
public interface IFieldExposer<T>
{
IEnumerable<Func<T, string>> GetFields();
}
Now, I implement it like this in my DataClass to expose the properties I would like to iterate. Note that I'm also exposing a property from my ChildClass:
using System;
using System.Collections.Generic;
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> GetFields()
{
return new List<Func<DataClass, string>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
class ChildClass
{
public string PropertyThree { get; set; }
}
I've also created extension methods for IFieldExposer<T> because I want to keep it simple and be able to simply call obj.Match(text, ignoreCase) everywhere else in my code. This method should tell me if my object is a match for my text. Here's the code for the ExtensionClass, which isn't working as expected:
using System;
using System.Linq.Expressions;
using System.Reflection;
public static class ExtensionClass
{
public static bool Match<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
Func<bool> expression = Expression.Lambda<Func<bool>>(obj.CreateExpressionTree(text, ignoreCase)).Compile();
return expression();
}
private static Expression CreateExpressionTree<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var exposedFields = obj.GetFields();
if (ignoreCase)
{
// How should I do convert these to lower too?
// exposedFields = exposedFields.Select(e => e.???.ToLower());
text = text.ToLower();
}
Expression textExp = Expression.Constant(text);
Expression orExpressions = Expression.Constant(false);
foreach (var field in exposedFields)
{
//How should I call the contains method on the string field?
Expression fieldExpression = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(obj), field.Method)); //this doesn't work
Expression contains = Expression.Call(fieldExpression, containsMethod, textExp);
orExpressions = Expression.Or(orExpressions, contains);
}
return orExpressions;
}
}
Please check the comments in the code above. I would like to know how to convert all my string properties to lowercase (if desired) and how to call string.Contains in each one of them. I get this error when I create my fieldExpression:
Method 'System.String <GetFields>b__12_0(DataClass)' declared on type 'DataClass+<>c' cannot be called with instance of type 'DataClass'
I don't have experience working with Expression Trees. I've spent hours reading docs and other answers for similar issues but I still can't understand how to achieve what I want... I have no clue what to do now.
I'm testing this in a console app so here's the main class if you want to build it yourself:
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var data = new DataClass
{
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass
{
PropertyThree = "Dolor"
}
};
var dataList = new List<DataClass> { data };
var results = dataList.Where(d => d.Match("dolor", true));
}
}
EDIT
I forgot to mention that my dataList should be IQueryable and I want to execute my code in SQL, that's why I'm trying to build the expression trees myself. So it appears my example code should be:
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable();
var results = query.Where(ExtensionClass.Match<DataClass>("lorem dolor"));
while my method becomes: (I'm following #sjb-sjb's answer and changed the GetFields() method in IFieldExposer<T> to a SelectedFields property)
public static Expression<Func<T, bool>> Match<T>(string text, bool ignoreCase) where T : IFieldExposer<T>
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "obj");
MemberExpression selectedFieldsExp = Expression.Property(parameter, "SelectedFields");
LambdaExpression lambda = Expression.Lambda(selectedFieldsExp, parameter).Compile();
[...]
}
And then it seems that I have to dinamically call selectedFieldsExp with Expression.Lambda. I came up with:
Expression.Lambda(selectedFieldsExp, parameter).Compile();
and that works, but I don't know how to properly call DynamicInvoke() for the lambda expression.
It throws Parameter count mismatch. if I call it without parameters and Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'DataClass'. if I do DynamicInvoke(parameter).
Any ideas?
Before getting to the implementation, there are some design flaws that needs to be fixed.
First, almost all query providers (except LINQ to Object which simply compiles the lambda expressions to delegates and executes them) don't support invocation expressions and custom (unknown) methods. That's because they do not execute the expressions, but translate them to something else (SQL for instance), and translation is based on pre knowledge.
One example of invocation expression are Func<...> delegates. So the first thing you should do is to use Expression<Func<...>> wherever you currently have Func<...>.
Second, the query expression trees are built statically, i.e. there is no real object instance you can use to obtain the metadata, so the idea of IFieldExposer<T> won't work. You'd need a statically exposed list of expressions like this:
class DataClass //: IFieldExposer<DataClass>
{
// ...
public static IEnumerable<Expression<Func<DataClass, string>>> GetFields()
{
return new List<Expression<Func<DataClass, string>>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
Then the signature of the method in question could be like this
public static Expression<Func<T, bool>> Match<T>(
this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
with usage like this
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable()
.Where(DataClass.GetFields().Match("lorem", true));
Now the implementation. The desired expression could be built purely with Expression class methods, but I'll show you an easier (IMHO) method, which composes expression from compile time expression by replacing the parameter(s) with other expression(s).
All you need is a small helper utility method for replacing lambda expression parameter with another expression:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}
Internally it uses ExpressionVistor to find each instance of the passed ParameterExpression and replace it with the passed Expression.
With this helper method, the implementation could be like this:
public static partial class ExpressionUtils
{
public static Expression<Func<T, bool>> Match<T>(this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
{
Expression<Func<string, bool>> match;
if (ignoreCase)
{
text = text.ToLower();
match = input => input.ToLower().Contains(text);
}
else
{
match = input => input.Contains(text);
}
// T source =>
var parameter = Expression.Parameter(typeof(T), "source");
Expression anyMatch = null;
foreach (var field in fields)
{
// a.PropertyOne --> source.PropertyOne
// b.Child.PropertyThree --> source.Child.PropertyThree
var fieldAccess = field.Body.ReplaceParameter(field.Parameters[0], parameter);
// input --> source.PropertyOne
// input --> source.Child.PropertyThree
var fieldMatch = match.Body.ReplaceParameter(match.Parameters[0], fieldAccess);
// matchA || matchB
anyMatch = anyMatch == null ? fieldMatch : Expression.OrElse(anyMatch, fieldMatch);
}
if (anyMatch == null) anyMatch = Expression.Constant(false);
return Expression.Lambda<Func<T, bool>>(anyMatch, parameter);
}
}
The input => input.ToLower().Contains(text) or input => input.Contains(text) is our compile time match expression, which we then replace the input parameter with the body of the passed Expression<Func<T, string>> lambda expressions, with their parameter replaced with a common parameter used in the final expression. The resulting bool expressions are combined with Expression.OrElse which is the equivalent of the C# || operator (while Expression.Or is for bitwise | operator and in general should not be used with logical operations). Same btw for && - use Expression.AndAlso and not Expression.And which is for bitwise &.
This process is pretty much the expression equivalent of the string.Replace. In case the explanations and code comments are not enough, you can step through the code and see the exact expression transformations and expression building process.
There is no need to get into the complexities of dynamically creating an Expression, because you can just invoke the Func delegate directly:
public interface IFieldExposer<T>
{
IEnumerable<Func<T,string>> SelectedFields { get; }
}
public static class FieldExposerExtensions
{
public static IEnumerable<Func<T,string>> MatchIgnoreCase<T>( this IEnumerable<Func<T,string>> stringProperties, T source, string matchText)
{
return stringProperties.Where(stringProperty => String.Equals( stringProperty( source), matchText, StringComparison.OrdinalIgnoreCase));
}
}
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> SelectedFields {
get {
return new Func<DataClass, string>[] { #this => #this.PropertyOne, #this => #this.Child.PropertyThree };
}
}
public override string ToString() => this.PropertyOne + " " + this.PropertyTwo + " " + this.Child.PropertyThree;
}
class ChildClass
{
public string PropertyThree { get; set; }
}
Then to use it,
class Program
{
static void Main(string[] args)
{
var data = new DataClass {
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass {
PropertyThree = "Dolor"
}
};
var data2 = new DataClass {
PropertyOne = "lorem",
PropertyTwo = "ipsum",
Child = new ChildClass {
PropertyThree = "doloreusement"
}
};
var dataList = new List<DataClass>() { data, data2 };
IEnumerable<DataClass> results = dataList.Where( d => d.SelectedFields.MatchIgnoreCase( d, "lorem").Any());
foreach (DataClass source in results) {
Console.WriteLine(source.ToString());
}
Console.ReadKey();
}
}
Following up on my comment above, I think you could do it like this:
class DataClass
{
…
static public Expression<Func<DataClass,bool>> MatchSelectedFields( string text, bool ignoreCase)
{
return #this => (
String.Equals( text, #this.PropertyOne, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
|| String.Equals( text, #this.Child.PropertyThree, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
);
}
}
Then the query is just
Expression<Func<DataClass,bool>> match = DataClass.MatchSelectedFields( "lorem", ignoreCase);
IEnumerable<DataClass> results = dataList.Where( d => match(d));
I wouldn't usually post a second answer but I thought it would be useful to see how to avoid dynamic modification of Expressions.
Caveat: I didn't actually try to compile it.

NHibernate IUserType and custom functions for HQL and Linq

In short I wanted to create a custom IUserType to represent IPAddress from .NET (as an inet type in postgresql) and to be able to query it with custom functions via HQL and Linq. I have trouble with implementing a custom function for Linq.
What I have thus far:
A) I am able to map it:
public class SomeEntityMapper : ClassMap<SomeEntity> {
public SomeEntityMapper() {
...
Map(x => x.IpAddressField)
.CustomSqlType("inet")
.CustomType<IPAddressUserType>()
...
}
}
IUserType implementation below:
[Serializable]
public class IPAddressUserType : IUserType
{
public new bool Equals(object x, object y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
if (x == null)
return 0;
return x.GetHashCode();
}
public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
{
if (names.Length == 0)
throw new InvalidOperationException("Expected at least 1 column");
if (rs.IsDBNull(rs.GetOrdinal(names[0])))
return null;
object value = rs[names[0]];
return value;
}
public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
{
NpgsqlParameter parameter = (NpgsqlParameter) cmd.Parameters[index];
parameter.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Inet;
if (value == null)
{
parameter.Value = DBNull.Value;
}
else
{
parameter.Value = value;
}
}
public object DeepCopy(object value)
{
if (value == null)
return null;
IPAddress copy = IPAddress.Parse(value.ToString());
return copy;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
if (cached == null)
return null;
if (IPAddress.TryParse((string)cached, out var address))
{
return address;
}
return null;
}
public object Disassemble(object value)
{
if (value == null)
return null;
return value.ToString();
}
public SqlType[] SqlTypes => new SqlType[] { new NpgsqlSqlType(DbType.String, NpgsqlTypes.NpgsqlDbType.Inet), };
public Type ReturnedType => typeof(IPAddress);
public bool IsMutable => false;
}
public class NpgsqlSqlType : SqlType
{
public NpgsqlDbType NpgDbType { get; }
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType)
: base(dbType)
{
NpgDbType = npgDbType;
}
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType, int length)
: base(dbType, length)
{
NpgDbType = npgDbType;
}
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType, byte precision, byte scale)
: base(dbType, precision, scale)
{
NpgDbType = npgDbType;
}
}
}
B) I am able to query it using HQL and a custom function (inet_equals) I implemented.
using(var session = _factory.OpenSession()) {
var q = session.CreateQuery("from SomeEntity as s WHERE inet_equals(s.IpAddressField, :ip)");
q.SetParameter("ip", IPAddress.Parse("4.3.2.1"), NHibernateUtil.Custom(typeof(IPAddressUserType)));
}
Custom Postgresql Dialect extending the HQL functions implementation below:
public class CustomPostgresqlDialect : PostgreSQL83Dialect
{
public CustomPostgresqlDialect()
{
RegisterFunction("inet_equals", new SQLFunctionTemplate(NHibernateUtil.Boolean, "(?1::inet = ?2::inet)"));
}
}
C) I want to be able to query it using LINQ like so:
using(var session = _factory.OpenSession()) {
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(IPAddress.Parse("4.3.2.1")));
}
Custom LINQ Generator for NHibernate provider below:
public static class InetExtensions
{
public static bool InetEquals(this IPAddress value, IPAddress other)
{
throw new NotSupportedException();
}
}
public class InetGenerator : BaseHqlGeneratorForMethod
{
public InetGenerator()
{
SupportedMethods = new[]
{
ReflectHelper.GetMethodDefinition(() => InetExtensions.InetEquals(default(IPAddress), default(IPAddress)))
};
}
public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
HqlExpression lhs = visitor.Visit(arguments[0]).AsExpression();
HqlExpression rhs = visitor.Visit(arguments[1]).AsExpression();
return treeBuilder.BooleanMethodCall(
"inet_equals",
new[]
{
lhs,
rhs
}
);
}
}
public class ExtendedLinqToHqlGeneratorsRegistry :
DefaultLinqToHqlGeneratorsRegistry
{
public ExtendedLinqToHqlGeneratorsRegistry()
: base()
{
this.Merge(new InetGenerator());
}
}
Unfortunately when using LINQ I get this exception:
HibernateException: Could not determine a type for class: System.Net.IPAddress
Interestingly, this is the exact same error I get if I omit the NHibernateUtil.Custom(typeof(IPAddressUserType)) parameter inside the HQL query.
Which leads me to believe I am on the right track, but I can't figure out what I may be missing exactly. I assume I need to inform NHibernate inside the Generator that this is a custom UserType (just like I did with the HQL query via the NHibernateUtil.Custom(typeof(IPAddressUserType)) parameter.
I found the solution.
To hint NHibernate to use the proper UserType use: MappedAs extension method, like so:
using(var session = _factory.OpenSession()) {
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(
IPAddress.Parse("4.3.2.1").MappedAs(NHibernateUtil.Custom(typeof(IPAddressUserType))
);
}
The year is 2019 and I'm still stuck on version 3.3.2.GA of NHibernate and the MappedAs extension method exists only on version 4.x onwards.
My scenario was the need to pass a large string as a parameter to a supported method in an HqlGeneratorForMethod.
I created the following class to store any large string to use as a parameter of any method throughout the application:
public class StringClob
{
public StringClob()
{
}
public StringClob(string value)
{
Value = value;
}
public virtual string Value { get; protected set; }
}
In order to link the NHibernateUtil.StringClob type with the string value I had the idea to create a mapping for my class without informing table mapping just the property (I use FluentNHibernate to map class):
public class StringClobMap : ClassMap<StringClob>
{
public StringClobMap()
{
Id(x => x.Value, "VALUE").CustomType("StringClob").CustomSqlType("VARCHAR(MAX)").Length(int.MaxValue / 2);
}
}
And now hypothetically following the example of #krdx the usage would look like this:
using(var session = _factory.OpenSession())
{
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(new StringClob("<large_string_here>")));
// ...
}
Therefore by passing the StringClob class as a parameter, NHibernate gets the custom type defined in the mapping.
I hope I helped those who still use NHibernate 3.3.2.GA.

Web Api user filtering response fields

I am creating a new web API and would like to allow the user to specify what fields get returned to them in the URL.
My current thoughts are:
For a sample model like this:
public class Value
{
public string ValueId { get; set; }
public int Number { get; set; }
public ValueInternal Internal { get; set; }
}
public class ValueInternal
{
public int Number { get; set; }
public string Something { get; set; }
}
and a URL like this
http://example.com/api/values/?_fields=Number,Internal(Something)
would return this
[
{
"Number": 0,
"Internal": {
"Number": 0
}
}
]
I have come up with the below method of achieving this, but it has some flaws. I.e. it couldn't handle if Internal was an enumerable of ValueInternal or has no support for include all or include all except, or if T and TResult are different types. Does anyone have any suggestions on how I can improve this or if there already exists a way of doing it that I am missing.
public static Expression<Func<T, TResult>> CreateSelector<T, TResult>() where TResult : new()
{
var property = "Number,Internal(Something)";
return arg => Process<T, TResult>(arg, default(TResult), property);
}
private static TResult Process<T, TResult>(T arg, TResult output, string propertyList) where TResult : new()
{
if (output == null)
{
output = new TResult();
}
if (string.IsNullOrEmpty(propertyList))
{
return output;
}
var properties = Regex.Split(propertyList, #"(?<!,[^(]+\([^)]+),");
foreach (var property in properties)
{
var propertyName = property;
var propertyInternalsMatch = Regex.Match(property, #"\(.*(?<!,[^(]+\([^)]+)\)");
var internalPropertyList = propertyInternalsMatch.Value;
if (!string.IsNullOrEmpty(internalPropertyList))
{
propertyName = property.Replace(internalPropertyList, "");
internalPropertyList = internalPropertyList.Replace("(", "");
internalPropertyList = internalPropertyList.Replace(")", "");
}
var tProperty = arg.GetType().GetProperty(propertyName);
if(tProperty == null) continue;
var tResultProperty = output.GetType().GetProperty(propertyName);
if(tResultProperty == null) continue;
if (tProperty.PropertyType.IsPrimitive || tProperty.PropertyType.IsValueType || (tProperty.PropertyType == typeof(string)))
{
tResultProperty.SetValue(output, tProperty.GetValue(arg));
}
else
{
var propertyInstance = Activator.CreateInstance(tResultProperty.PropertyType);
tResultProperty.SetValue(output, Process(tProperty.GetValue(arg), propertyInstance, internalPropertyList));
}
}
return output;
}
After a bit more reading I think I want to do something like the answer to this question LINQ : Dynamic select but that still has the same flaws my solution had
If you use OData support on your ASP .NET Web API you can jus use $select, but if you don't want to use it or your underlying system can't be easy queried using Linq, you can use a custom contract resolver, but in this case you are just reducing the serialization size, not the internal data traffic.
public class FieldsSelectContractResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.GetIsSpecified = (t) =>
{
var fields = HttpContext.Current.Request["fields"];
if (fields != null)
{
return fields.IndexOf(member.Name, StringComparison.OrdinalIgnoreCase) > -1;
}
return true;
};
return property;
}
}
and in WebApiConfig.cs set the custom contract resolver:
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new FieldsSelectContractResolver();

c# convert predicate between each other

I'm developing and multi-tier application solution and my solution looks like this
Business.DomainObject
Business.Process (* Actual business layer)
Data.Mapper
Data.Repository
Data.Sql.Entity (* Actual data layer that has .dbml file)
My Business.Process project (business layer) is only knows Business.DomainObject and Data.Repository projects, so doesn't know and not in relationship with Data.Sql.Entity project
I'm sending business (domain) objects to repository and do mapping inside this project, then I'm doing CRUD process inside repository layer with relationship data layers.
My traditional domain object is like this, it has only properties
public class UserBO
{
public string Email { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
My business layer class is like this,
Repository r = new UserRepository();
r.Insert(myUserDomainObject);
Everything is OK, but I have some problems about my "predicate" queries.
I have a method in my repository interface
bool IsExists(Expression<Func<BusinessObject,bool>> predicate);
and use it in my business layer like this;
Repository r = new UserRepository(); <br/>
r.IsExists(p => p.Email.Equals("email address"));
as you can see, its parameter is "business object", but my actual repository (that connects with database) using "dataobject" that is inside my dbml file.
public override bool IsExists(Expression<Func<DataObject, bool>> predicate){
return _table.Where(predicate).Any();
}
I want convert this two predicate,
for example I'll send UserBO and convert to UserDataObject
how can i do that?
You have to analyze the parameters and the body of the LambdaExpression and build a new one. You can access the body it using predicate.Body and the parameters using predicate.Parameters. The new expression has a parameter of type UserBO instead of UserDataObject. In addition, the ParameterExpression(s) in the body must use the new parameter.
Of course, this assumes that it is a simple scenario where the BO and the DataObject have the same properties. More complex transformations are possible but need a deeper analysis of the expression tree.
In order to vive you a starting Point, I out together a sample with a very similar BusinessObject and DataObject. The key component is a class derived from ExpressionVisitor that performs the conversion:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConvertExpression
{
public class BusinessObject
{
public int Value { get; set; }
}
public class DataObject
{
public int Value { get; set; }
}
internal class ExpressionConverter : ExpressionVisitor
{
public Expression Convert(Expression expr)
{
return Visit(expr);
}
private ParameterExpression replaceParam;
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) == typeof(Func<BusinessObject, bool>))
{
replaceParam = Expression.Parameter(typeof(DataObject), "p");
return Expression.Lambda<Func<DataObject, bool>>(Visit(node.Body), replaceParam);
}
return base.VisitLambda<T>(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(BusinessObject))
return replaceParam; // Expression.Parameter(typeof(DataObject), "p");
return base.VisitParameter(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(BusinessObject))
{
var member = typeof(DataObject).GetMember(node.Member.Name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).FirstOrDefault();
if (member == null)
throw new InvalidOperationException("Cannot identify corresponding member of DataObject");
return Expression.MakeMemberAccess(Visit(node.Expression), member);
}
return base.VisitMember(node);
}
}
public class ConvertExpression
{
public static void Main()
{
BusinessObject[] bos = { new BusinessObject() { Value = 123 }, new BusinessObject() { Value = 246 } };
DataObject[] dos = { new DataObject() { Value = 123 }, new DataObject() { Value = 246 } };
Expression<Func<BusinessObject, bool>> boExpr = x => x.Value == 123;
var conv = new ExpressionConverter();
Expression<Func<DataObject, bool>> doExpr = (Expression<Func<DataObject, bool>>) conv.Convert(boExpr);
var selBos = bos.Where(boExpr.Compile());
Console.WriteLine("Matching BusinessObjects: {0}", selBos.Count());
foreach (var bo in selBos)
Console.WriteLine(bo.Value);
var compDoExpr = doExpr.Compile();
var selDos = dos.Where(doExpr.Compile());
Console.WriteLine("Matching DataObjects: {0}", selDos.Count());
foreach (var dataObj in selDos)
Console.WriteLine(dataObj.Value);
Console.ReadLine();
}
}
}
If you need it i created a small fluent library to create lambda functions on the fly without directly coping with System.Linq.Expressions. Between the other things it contains exactly what you need, just to give an example to accomply the compare on the city you could do like this:
//Cached somewhere
var compareLambda= ExpressionUtil.GetComparer<CityBO>(p =>
p.Id.Value,ComparaisonOperator.Equal);
//Then in the execution
Repository.IsExists(p=>compareLambda(p,city id));
The code and documentation are here: Kendar Expression Builder with the unit tests that are pretty self explanatory
While the nuget package is here: Nuget Expression Builder
your code works well with single property like
Repository.IsExists(p => p.Email.Equals("abc#xyz.com"));
but as i mentioned, my domain object classes have some nested class properties like "City", when i try this
Repository.IsExists(p => p.City.Id.Equals(city id));
it throws an exception;
Property
'Business.DomainObject.SystemCommon.ExtendedProperty.PrimaryKey
Id' is not defined for type
'Data.Sql.Entity.LinqDataContext.City'
i understand this exception, because my City class like this;
public class CityBO : IDomainObject
{
public PrimaryKey Id { get; set; }
public string Name { get; set; }
public EntityReferance<CountryBO> Country { get; set; }
public LocationInfoBO Location { get; set; }
public StatusInfo Status { get; set; }
}
and PrimaryKey property (class) is
public class PrimaryKey
{
public string Value { get; set; }
}
I'm using this property, because of my entities have different Primary Key type and also I'm planning implementation of Nosql db in future..
i want thank you for your code, it is useful for me when I'm query with single property.
regards
giving credit to #Markus, here is a generic converter class...
internal class ExpressionConverter<TInput, TOutput> : ExpressionVisitor
{
public Expression<Func<TOutput, bool>> Convert(Expression<Func<TInput, bool>> expression)
{
return (Expression<Func<TOutput, bool>>)Visit(expression);
}
private ParameterExpression replaceParam;
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) == typeof(Func<TInput, bool>))
{
replaceParam = Expression.Parameter(typeof(TOutput), "p");
return Expression.Lambda<Func<TOutput, bool>>(Visit(node.Body), replaceParam);
}
return base.VisitLambda<T>(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TInput))
return replaceParam;
return base.VisitParameter(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TInput))
{
var member = typeof(TOutput).GetMember(node.Member.Name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).FirstOrDefault();
if (member == null)
throw new InvalidOperationException("Cannot identify corresponding member of DataObject");
return Expression.MakeMemberAccess(Visit(node.Expression), member);
}
return base.VisitMember(node);
}
}

Using anonymous type in lambda to map or partially clone any number of an object's properties

I want to make a cloning or mapping function where I can specify (once) which properties to copy to the target object.
The whole point here was a usage that looks something like this
(inspired by using GroupBy),
var botchedOrder = db.BuildingOrders.Find(id);
var redo = db.BuildingOrders.Create();
botchedOrder.MapTo(redo, x => new { x.BasePrice, x.FileAttachments, x.Notes });
This is over my head, but I was guessing at something like this,
public static void MapTo<TObject, TProps>(
this TObject source,
TObject target,
Action<TProps> properties) //?
{
//copy the defined properties from source to target somehow?
//originally I thought I could make an array of delegates..
}
If this works then I can more easily handle different sets of properties in different ways when I am explicitly cloning or mapping objects. I'd like to stick w/ .NET to do this.
EDIT: forgot to indicate void
I'd recommend looking at AutoMapper since it kind of solves same kinds of problems. Also, different ORM sources (like Massive, Petapoco etc.) all have some implementations (since they need to map data from a DB to an object).
Basically, it works either using reflection - to iterate over given fields/properties and set them to another object (also via refleciton). There are approaches to dynamic method generation using IL Generator or Expression Trees that wrap all the reflection code into one nice and fast method generated and compiled at runtime (thus giving performance boost).
Another way to do would be using dynamics or Dictionaries instead of anonymous class - that would remove "anonimity" problem and would just require to map specific keys on to properties - may be done with reflection again (using for example [MappingAttribute("Key")] for distinction - attribute is a fiction, just to give a general idea).
This is the most succinct form I can think of without dropping down to reflection, but it does involve repeating property names, so I'm not sure if it's exactly what you want.
public static void MapTo<TObject>(this TObject source, TObject target, params Action<TObject, TObject>[] properties)
{
foreach (var property in properties)
{
property(source, target);
}
}
Called like:
void Copy(FooBar source, FooBar target)
{
source.MapTo(target, (s,t) => t.Foo = s.Foo,
(s,t) => t.Bar = s.Bar,
(t,s) => t.Baz = s.Baz);
}
class FooBar
{
public string Foo { get; set; }
public string Bar { get; set; }
public string Baz { get; set; }
}
However, it's more verbose that just doing:
void Copy(FooBar source, FooBar target)
{
target.Foo = source.Foo;
target.Bar = source.Bar;
target.Baz = source.Baz;
}
Is there anything else going on in your copy that make the last example invalid? If not, I would just keep it simple and go for that.
Here is a basic dynamic mapper I wrote. I used a slightly different approach as I extended object and instead of specifying the properties, used ignore properties.
public static class ObjectExtensions
{
public static void CopyFrom(this object Instance, object Source)
{
ObjectExtensions.CopyFrom(Instance, Source, false, null);
}
public static void CopyFrom(this object Instance,
object Source,
IEnumerable<string> IgnoreProperties)
{
ObjectExtensions.CopyFrom(Instance, Source, false, IgnoreProperties);
}
public static void CopyFrom(this object Instance,
object Source,
bool ThrowOnPropertyMismatch,
IEnumerable<string> IgnoreProperties)
{
Type sourceType = Source.GetType();
BindingFlags publicInstanceFlags = BindingFlags.Public
| BindingFlags.Instance;
PropertyInfo[] sourceProperties =
sourceType.GetProperties(publicInstanceFlags);
Type instanceType = Instance.GetType();
foreach (PropertyInfo sourceProperty in sourceProperties)
{
if (IgnoreProperties == null
|| (IgnoreProperties.Count() > 0
&& !IgnoreProperties.Contains(sourceProperty.Name)))
{
PropertyInfo instanceProperty =
instanceType.GetProperty(sourceProperty.Name, publicInstanceFlags);
if (instanceProperty != null
&& instanceProperty.PropertyType == sourceProperty.PropertyType
&& instanceProperty.GetSetMethod() != null
&& instanceProperty.GetSetMethod().IsPublic)
{
instanceProperty.SetValue(Instance,
sourceProperty.GetValue(Source, null),
null);
}
else
if (ThrowOnPropertyMismatch
&& instanceProperty.PropertyType != sourceProperty.PropertyType)
{
throw new InvalidCastException(
string.Format("Unable to cast source {0}.{1} to destination {2}.{3}.",
Source.GetType().Name,
sourceProperty.Name,
Instance.GetType().Name,
instanceProperty.Name));
}
}
}
}
A common way of doing this is by using expression trees which can represent the way certain types should map to one another. A primitive stripped down example of such is:
public static void MapTo<TInput, TOutput>(this TInput input, TOutput output, Expression<Func<TInput, TOutput, bool>> expression)
where TInput : class
where TOutput : class
{
if (expression == null)
throw new ArgumentNullException("expression");
Stack<Expression> unhandeledExpressions = new Stack<Expression>();
unhandeledExpressions.Push(expression.Body);
while (unhandeledExpressions.Any())
{
Expression unhandeledExpression = unhandeledExpressions.Pop();
switch (unhandeledExpression.NodeType)
{
case ExpressionType.AndAlso:
{
BinaryExpression binaryExpression = (BinaryExpression)unhandeledExpression;
unhandeledExpressions.Push(binaryExpression.Left);
unhandeledExpressions.Push(binaryExpression.Right);
}
break;
case ExpressionType.Equal:
{
BinaryExpression binaryExpression = (BinaryExpression)unhandeledExpression;
MemberExpression leftArgumentExpression = binaryExpression.Left as MemberExpression;
MemberExpression rightArgumentExpression = binaryExpression.Right as MemberExpression;
if (leftArgumentExpression == null || rightArgumentExpression == null)
throw new InvalidOperationException("Can only map to member expressions");
output.GetType().GetProperty(leftArgumentExpression.Member.Name).SetValue(
output, input.GetType().GetProperty(rightArgumentExpression.Member.Name).GetValue(input, null), null);
}
break;
default:
throw new InvalidOperationException("Expression type not supported");
}
}
}
}
which can be used in the following way:
class SourceType
{
public string Name { get; set; }
public int Number { get; set; }
}
class DestinationType
{
public string CustName { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = new SourceType()
{
Name = "Test",
Number = 22
};
var destination = new DestinationType();
source.MapTo(destination, (src, dst) => dst.CustName == src.Name && dst.Age == src.Number);
bool assert = source.Name == destination.CustName && source.Number == destination.Age;
}
}
The advantage of this approach is that this allows you to define your own mapping 'language' which you can make as complex/extensive as you want.
Still I recommend you to use a pre-built solution like AutoFaq or AutoMapper. Good luck
ok, I think I have something working using an Expression and Reflection.
thing1.MapTo(thing2, x => new { x.Prop1, x.Prop2 });
and
public static void MapTo<T, P>(
this T source, T target, Expression<Func<T, P>> expr)
{
(expr.Body as NewExpression).Members.ToList()
.ForEach(m => source.Copy(target, m.Name));
}
public static void Copy<T>(this T source, T target, string prop)
{
var p = typeof(T).GetProperty(prop);
p.SetValue(target, p.GetValue(source, null), null);
}
I'm not sure the methods have the best names but it's a start and it allows the usage I was hoping for. Any problems with this approach?

Categories

Resources