Skip to content

Commit

Permalink
Merge pull request #1695 from linq2db/dynamic_store_mapping_support
Browse files Browse the repository at this point in the history
Dynamic store configuration improvements
  • Loading branch information
MaceWindu committed Jun 26, 2019
2 parents ac1a78a + 5f207e2 commit ab3ab58
Show file tree
Hide file tree
Showing 16 changed files with 1,342 additions and 173 deletions.
12 changes: 12 additions & 0 deletions Source/LinqToDB/Expressions/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,18 @@ public static Expression GetBody(this LambdaExpression lambda, Expression exprTo
e == lambda.Parameters[1] ? exprToReplaceParameter2 : e);
}

/// <summary>
/// Returns the body of <paramref name="lambda"/> but replaces the first three parameters of
/// that lambda expression with the given replace expressions.
/// </summary>
public static Expression GetBody(this LambdaExpression lambda, Expression exprToReplaceParameter1, Expression exprToReplaceParameter2, Expression exprToReplaceParameter3)
{
return Transform(lambda.Body, e =>
e == lambda.Parameters[0] ? exprToReplaceParameter1 :
e == lambda.Parameters[1] ? exprToReplaceParameter2 :
e == lambda.Parameters[2] ? exprToReplaceParameter3 : e);
}

static IEnumerable<T> Transform<T>(ICollection<T> source, Func<T,T> func)
where T : class
{
Expand Down
46 changes: 46 additions & 0 deletions Source/LinqToDB/Expressions/MappingExpressionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using LinqToDB.Common;
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace LinqToDB.Extensions
{
static class MappingExpressionsExtensions
{
public static TExpression GetExpressionFromExpressionMember<TExpression>(this Type type, string memberName)
where TExpression : Expression
{
var members = type.GetStaticMembersEx(memberName);

if (members.Length == 0)
throw new LinqToDBException($"Static member '{memberName}' for type '{type.Name}' not found");

if (members.Length > 1)
throw new LinqToDBException($"Ambiguous members '{memberName}' for type '{type.Name}' has been found");

switch (members[0])
{
case PropertyInfo propInfo:
{
if (propInfo.GetValue(null, null) is TExpression expression)
return expression;

throw new LinqToDBException($"Property '{memberName}' for type '{type.Name}' should return expression");
}
case MethodInfo method:
{
if (method.GetParameters().Length > 0)
throw new LinqToDBException($"Method '{memberName}' for type '{type.Name}' should have no parameters");

if (method.Invoke(null, Array<object>.Empty) is TExpression expression)
return expression;

throw new LinqToDBException($"Method '{memberName}' for type '{type.Name}' should return expression");
}
default:
throw new LinqToDBException(
$"Member '{memberName}' for type '{type.Name}' should be static property or method");
}
}
}
}
4 changes: 2 additions & 2 deletions Source/LinqToDB/Linq/Builder/TableBuilder.TableContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void SetLoadWithBindings(Type objectType, ParameterExpression parentObject, List
if (member.MemberInfo.IsDynamicColumnPropertyEx())
{
var typeAcc = TypeAccessor.GetAccessor(member.MemberInfo.ReflectedTypeEx());
var setter = new MemberAccessor(typeAcc, member.MemberInfo).SetterExpression;
var setter = new MemberAccessor(typeAcc, member.MemberInfo, EntityDescriptor).SetterExpression;

exprs.Add(Expression.Invoke(setter, parentObject, ex));
}
Expand Down Expand Up @@ -1248,7 +1248,7 @@ protected virtual ISqlExpression GetField(Expression expression, int level, bool
Name = fieldName,
PhysicalName = fieldName,
ColumnDescriptor = new ColumnDescriptor(Builder.MappingSchema, new ColumnAttribute(fieldName),
new MemberAccessor(EntityDescriptor.TypeAccessor, memberExpression.Member))
new MemberAccessor(EntityDescriptor.TypeAccessor, memberExpression.Member, EntityDescriptor))
};

SqlTable.Add(newField);
Expand Down
42 changes: 1 addition & 41 deletions Source/LinqToDB/Mapping/AssociationDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,47 +224,7 @@ public LambdaExpression GetQueryMethod(Type parentType, Type objectType)
throw new ArgumentException($"Member '{MemberInfo.Name}' has no declaring type");

if (!string.IsNullOrEmpty(ExpressionQueryMethod))
{
var members = type.GetStaticMembersEx(ExpressionQueryMethod);

if (members.Length == 0)
throw new LinqToDBException($"Static member '{ExpressionQueryMethod}' for type '{type.Name}' not found");

if (members.Length > 1)
throw new LinqToDBException($"Ambiguous members '{ExpressionQueryMethod}' for type '{type.Name}' has been found");

var propInfo = members[0] as PropertyInfo;

if (propInfo != null)
{
var value = propInfo.GetValue(null, null);
if (value == null)
return null;

queryExpression = value as Expression;
if (queryExpression == null)
throw new LinqToDBException($"Property '{ExpressionQueryMethod}' for type '{type.Name}' should return expression");
}
else
{
var method = members[0] as MethodInfo;
if (method != null)
{
if (method.GetParameters().Length > 0)
throw new LinqToDBException($"Method '{ExpressionQueryMethod}' for type '{type.Name}' should have no parameters");
var value = method.Invoke(null, Array<object>.Empty);
if (value == null)
return null;

queryExpression = value as Expression;
if (queryExpression == null)
throw new LinqToDBException($"Method '{ExpressionQueryMethod}' for type '{type.Name}' should return expression");
}
}
if (queryExpression == null)
throw new LinqToDBException(
$"Member '{ExpressionQueryMethod}' for type '{type.Name}' should be static property or method");
}
queryExpression = type.GetExpressionFromExpressionMember<Expression>(ExpressionQueryMethod);
else
queryExpression = ExpressionQuery;

Expand Down
82 changes: 82 additions & 0 deletions Source/LinqToDB/Mapping/DynamicColumnAccessorAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Linq.Expressions;

namespace LinqToDB.Mapping
{
/// <summary>
/// Configure setter and getter methods for dynamic columns.
/// </summary>
/// <remarks>
/// Expected signatures for getter and setter:
/// <code>
/// // should return true and value of property, if property value found in storage
/// // should return false if property value not found in storage
/// static object Getter(Entity object, string propertyName, object defaultValue);
/// // or
/// object this.Getter(string propertyName, object defaultValue);
/// // where defaultValue is default value for property type for current MappingSchema
///
/// static void Setter(Entity object, string propertyName, object value)
/// or
/// void this.Setter(string propertyName, object value)
/// </code>
/// </remarks>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DynamicColumnAccessorAttribute : Attribute, IConfigurationProvider
{
/// <summary>
/// Gets or sets mapping schema configuration name, for which this attribute should be taken into account.
/// <see cref="ProviderName"/> for standard names.
/// Attributes with <c>null</c> or empty string <see cref="Configuration"/> value applied to all configurations (if no attribute found for current configuration).
/// </summary>
public string Configuration { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property setter method.
/// </summary>
public string SetterMethod { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property getter method.
/// </summary>
public string GetterMethod { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property setter expression method or property. Method or property
/// must be static.
/// </summary>
public string SetterExpressionMethod { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property getter expression method or property. Method or property
/// must be static.
/// </summary>
public string GetterExpressionMethod { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property set expression.
/// </summary>
public LambdaExpression SetterExpression { get; set; }

/// <summary>
/// Gets or sets name of dynamic properties property get expression.
/// </summary>
public LambdaExpression GetterExpression { get; set; }

protected internal void Validate()
{
var setters = 0;
var getters = 0;
if (SetterMethod != null) setters++;
if (SetterExpressionMethod != null) setters++;
if (SetterExpression != null) setters++;
if (GetterMethod != null) getters++;
if (GetterExpressionMethod != null) getters++;
if (GetterExpression != null) getters++;

if (setters != 1 || getters != 1)
throw new LinqToDBException($"{nameof(DynamicColumnAccessorAttribute)} should have exactly one setter and getter configured.");
}
}
}
2 changes: 1 addition & 1 deletion Source/LinqToDB/Mapping/DynamicColumnsStoreAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace LinqToDB.Mapping
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DynamicColumnsStoreAttribute : Attribute
public class DynamicColumnsStoreAttribute : Attribute, IConfigurationProvider
{
/// <summary>
/// Gets or sets mapping schema configuration name, for which this attribute should be taken into account.
Expand Down

0 comments on commit ab3ab58

Please sign in to comment.