This commit is contained in:
grzegorz.russek
2018-08-22 20:05:49 +00:00
parent 22c94619c6
commit fe36953bd5
19 changed files with 801 additions and 62 deletions

View File

@@ -31,7 +31,7 @@ using System;
namespace DynamORM.Builders
{
/// <summary>Dynamic delete query builder interface.</summary>
/// <remarks>This interface it publically available. Implementation should be hidden.</remarks>
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder
{
/// <summary>Execute this builder.</summary>

View File

@@ -31,7 +31,7 @@ using System;
namespace DynamORM.Builders
{
/// <summary>Dynamic insert query builder interface.</summary>
/// <remarks>This interface it publically available. Implementation should be hidden.</remarks>
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
public interface IDynamicInsertQueryBuilder : IDynamicQueryBuilder
{
/// <summary>Execute this builder.</summary>

View File

@@ -34,7 +34,7 @@ using DynamORM.Helpers;
namespace DynamORM.Builders
{
/// <summary>Dynamic query builder base interface.</summary>
/// <remarks>This interface it publically available. Implementation should be hidden.</remarks>
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
public interface IDynamicQueryBuilder : IExtendedDisposable
{
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>

View File

@@ -33,7 +33,7 @@ using System.Data;
namespace DynamORM.Builders
{
/// <summary>Dynamic select query builder interface.</summary>
/// <remarks>This interface it publically available. Implementation should be hidden.</remarks>
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
public interface IDynamicSelectQueryBuilder : IDynamicQueryBuilder ////, IEnumerable<object>
{
/// <summary>Execute this builder.</summary>

View File

@@ -31,7 +31,7 @@ using System;
namespace DynamORM.Builders
{
/// <summary>Dynamic update query builder interface.</summary>
/// <remarks>This interface it publically available. Implementation should be hidden.</remarks>
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder
{
/// <summary>Execute this builder.</summary>

View File

@@ -227,7 +227,7 @@ namespace DynamORM.Builders.Implementation
reader(rdr);
}
/// <summary>Execute this builder as a data reader, but
/// <summary>Execute this builder as a data reader, but
/// first makes a full reader copy in memory.</summary>
/// <param name="reader">Action containing reader.</param>
public virtual void ExecuteCachedDataReader(Action<IDataReader> reader)

View File

@@ -104,11 +104,14 @@
<Compile Include="Mapper\DynamicPropertyInvoker.cs" />
<Compile Include="Mapper\DynamicTypeMap.cs" />
<Compile Include="Mapper\IgnoreAttribute.cs" />
<Compile Include="Validation\RequiredAttribute.cs" />
<Compile Include="Mapper\TableAttribute.cs" />
<Compile Include="Mapper\DynamicCast.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DynamicExpando.cs" />
<Compile Include="Helpers\IFinalizerDisposable.cs" />
<Compile Include="Validation\ValidateResult.cs" />
<Compile Include="Validation\ValidationResult.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -324,7 +324,7 @@ namespace DynamORM
{
return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)));
}
/// <summary>Adds to the <code>FROM</code> clause using <see cref="Type"/>.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <param name="alias">Table alias.</param>
@@ -1387,7 +1387,7 @@ namespace DynamORM
protected virtual DbType ReadSchemaType(dynamic schema)
{
Type type = (Type)schema.DATATYPE;
// Small hack for SQL Server Provider
if (type == typeof(string) && Provider != null && Provider.GetType() == typeof(System.Data.SqlClient.SqlClientFactory))
{
@@ -1398,6 +1398,7 @@ namespace DynamORM
{
case "varchar":
return DbType.AnsiString;
case "nvarchar":
return DbType.String;
}
@@ -1466,7 +1467,7 @@ namespace DynamORM
v.Value.Column != null ? v.Value.Column.IsUnique : null,
col.HasValue ? col.Value.IsUnique : false).Value,
AllowNull = DynamicExtensions.CoalesceNullable<bool>(
v.Value.Column != null ? v.Value.Column.AllowNull : true,
v.Value.Column != null ? v.Value.Column.AllowNull : true,
col.HasValue ? col.Value.AllowNull : true).Value,
Size = DynamicExtensions.CoalesceNullable<int>(
v.Value.Column != null ? v.Value.Column.Size : null,

View File

@@ -1605,7 +1605,7 @@ namespace DynamORM
return defaultValue;
}
/// <summary>Delegate fro try parse function of a type.</summary>
/// <summary>Delegate from try parse function of a type.</summary>
/// <typeparam name="T">Type which implements this function.</typeparam>
/// <param name="value">Value to parse.</param>
/// <param name="result">Resulting value.</param>

View File

@@ -27,7 +27,6 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;

View File

@@ -411,7 +411,7 @@ namespace DynamORM.Helpers.Dynamics
return "{DynamicParser::Node::SetMember::Disposed}";
return string.Format("({0}.{1} = {2})", Host.Sketch(), Name.Sketch(), Value.Sketch());
}
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
@@ -438,7 +438,6 @@ namespace DynamORM.Helpers.Dynamics
catch
{
}
}
base.Dispose(disposing);
@@ -650,7 +649,6 @@ namespace DynamORM.Helpers.Dynamics
catch
{
}
}
base.Dispose(disposing);
@@ -735,7 +733,7 @@ namespace DynamORM.Helpers.Dynamics
var node = Arguments[i] as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
@@ -745,8 +743,8 @@ namespace DynamORM.Helpers.Dynamics
Array.Clear(Arguments, 0, Arguments.Length);
}
}
catch
{
catch
{
}
Arguments = null;
@@ -839,7 +837,6 @@ namespace DynamORM.Helpers.Dynamics
return string.Format("{0}.{1}{2}", Host.Sketch(), Name.Sketch(), Arguments == null ? "()" : Arguments.Sketch(brackets: "()".ToCharArray()));
}
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
@@ -875,7 +872,6 @@ namespace DynamORM.Helpers.Dynamics
base.Dispose(disposing);
}
}
#endregion Method
@@ -1049,7 +1045,7 @@ namespace DynamORM.Helpers.Dynamics
return string.Format("({0} {1})", Operation, Host.Sketch());
}
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
@@ -1218,11 +1214,11 @@ namespace DynamORM.Helpers.Dynamics
{
if (node != null)
{
Node parent = Host;
Node parent = Host;
while (parent != null)
{
if (object.ReferenceEquals(parent, node))
if (object.ReferenceEquals(parent, node))
return true;
parent = parent.Host;

View File

@@ -80,8 +80,8 @@ namespace DynamORM.Helpers.Dynamics
{
Type type = v.ReturnType == typeof(void) ?
Expression.GetActionType(v.GetParameters().Select(t => t.ParameterType).ToArray()) :
Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReturnType }).ToArray());
Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReturnType }).ToArray());
return Delegate.CreateDelegate(type, _proxy, v.Name);
}
catch (ArgumentException)

View File

@@ -32,6 +32,7 @@ using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using DynamORM.Validation;
namespace DynamORM.Mapper
{
@@ -47,8 +48,11 @@ namespace DynamORM.Mapper
public int Ordinal { get; set; }
}
private Type _arrayType;
private bool _genericEnumerable;
/// <summary>Gets the array type of property if main type is a form of collection.</summary>
public Type ArrayType { get; private set; }
/// <summary>Gets a value indicating whether this property is in fact a generic list.</summary>
public bool IsGnericEnumerable { get; private set; }
/// <summary>Gets the type of property.</summary>
public Type Type { get; private set; }
@@ -65,6 +69,9 @@ namespace DynamORM.Mapper
/// <summary>Gets type column description.</summary>
public ColumnAttribute Column { get; private set; }
/// <summary>Gets type list of property requirements.</summary>
public List<RequiredAttribute> Requirements { get; private set; }
/// <summary>Gets a value indicating whether this <see cref="DynamicPropertyInvoker"/> is ignored in some cases.</summary>
public bool Ignore { get; private set; }
@@ -80,21 +87,22 @@ namespace DynamORM.Mapper
Type = property.PropertyType;
object[] ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
Requirements = property.GetCustomAttributes(typeof(RequiredAttribute), false).Cast<RequiredAttribute>().ToList();
Ignore = ignore != null && ignore.Length > 0;
_arrayType = Type.IsArray ? Type.GetElementType() :
Type.IsGenericEnumerable() ? Type.GetGenericArguments().First() :
IsGnericEnumerable = Type.IsGenericEnumerable();
ArrayType = Type.IsArray ? Type.GetElementType() :
IsGnericEnumerable ? Type.GetGenericArguments().First() :
Type;
_genericEnumerable = Type.IsGenericEnumerable();
IsDataContract = ArrayType.GetCustomAttributes(false).Any(x => x.GetType().Name == "DataContractAttribute");
IsDataContract = _arrayType.GetCustomAttributes(false).Any(x => x.GetType().Name == "DataContractAttribute");
if (_arrayType.IsArray)
if (ArrayType.IsArray)
throw new InvalidOperationException("Jagged arrays are not supported");
if (_arrayType.IsGenericEnumerable())
if (ArrayType.IsGenericEnumerable())
throw new InvalidOperationException("Enumerables of enumerables are not supported");
Column = attr;
@@ -149,20 +157,20 @@ namespace DynamORM.Mapper
{
if (!Type.IsAssignableFrom(val.GetType()))
{
if (Type.IsArray || _genericEnumerable)
if (Type.IsArray || IsGnericEnumerable)
{
if (val != null)
{
var lst = (val as IEnumerable<object>).Select(x => GetElementVal(_arrayType, x)).ToList();
var lst = (val as IEnumerable<object>).Select(x => GetElementVal(ArrayType, x)).ToList();
value = Array.CreateInstance(_arrayType, lst.Count);
value = Array.CreateInstance(ArrayType, lst.Count);
int i = 0;
foreach (var e in lst)
((Array)value).SetValue(e, i++);
}
else
value = Array.CreateInstance(_arrayType, 0);
value = Array.CreateInstance(ArrayType, 0);
}
else
value = GetElementVal(Type, val);
@@ -198,7 +206,17 @@ namespace DynamORM.Mapper
else if (type.IsEnum && val.GetType().IsValueType)
return Enum.ToObject(type, val);
else if (type.IsEnum)
return Enum.Parse(type, val.ToString());
try
{
return Enum.Parse(type, val.ToString());
}
catch (ArgumentException)
{
if (nullable)
return null;
throw;
}
else if (Type == typeof(string) && val.GetType() == typeof(Guid))
return val.ToString();
else if (Type == typeof(Guid) && val.GetType() == typeof(string))
@@ -209,7 +227,17 @@ namespace DynamORM.Mapper
else if (IsDataContract)
return val.Map(type);
else
return Convert.ChangeType(val, type);
try
{
return Convert.ChangeType(val, type);
}
catch
{
if (nullable)
return null;
throw;
}
}
#region Type command cache

View File

@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using DynamORM.Validation;
namespace DynamORM.Mapper
{
@@ -138,6 +139,57 @@ namespace DynamORM.Mapper
return destination;
}
/// <summary>Validates the object.</summary>
/// <param name="val">The value.</param>
/// <returns>List of not valid results.</returns>
public IList<ValidationResult> ValidateObject(object val)
{
var result = new List<ValidationResult>();
if (val == null || val.GetType() != Type)
return null;
foreach (var prop in ColumnsMap.Values)
{
if (prop.Requirements == null || !prop.Requirements.Any())
continue;
var v = prop.Get(val);
foreach (var r in prop.Requirements)
{
var valid = r.ValidateSimpleValue(prop, v);
if (valid == ValidateResult.Valid)
{
if (prop.Type.IsArray || prop.IsGnericEnumerable)
{
var map = DynamicMapperCache.GetMapper(prop.ArrayType);
foreach (var item in val as IEnumerable<object>)
result.AddRange(map.ValidateObject(item));
}
continue;
}
if (valid == ValidateResult.NotSupported)
{
result.AddRange(DynamicMapperCache.GetMapper(prop.Type).ValidateObject(v));
continue;
}
result.Add(new ValidationResult()
{
Property = prop,
Requirement = r,
Value = v,
});
}
}
return result;
}
private IEnumerable<MemberInfo> GetAllMembers(Type type)
{
if (type.IsInterface)

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using DynamORM.Mapper;
namespace DynamORM.Validation
{
/// <summary>Required attribute can be used to validate fields in objects using mapper class.</summary>
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : Attribute
{
/// <summary>Gets or sets minimum value or length of field.</summary>
public decimal? Min { get; set; }
/// <summary>Gets or sets maximum value or length of field.</summary>
public decimal? Max { get; set; }
/// <summary>Gets or sets pattern to verify.</summary>
public Regex Pattern { get; set; }
/// <summary>Gets or sets a value indicating whether property value is required or not.</summary>
public bool Required { get; set; }
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
/// <param name="required">This field will be required.</param>
public RequiredAttribute(bool required = true)
{
Required = required;
}
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
/// <param name="val">Limiting value to set.</param>
/// <param name="max">Whether set maximum parameter (true) or minimum parameter (false).</param>
/// <param name="required">This field will be required.</param>
public RequiredAttribute(float val, bool max, bool required = true)
{
if (max)
Max = (decimal)val;
else
Min = (decimal)val;
Required = required;
}
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
/// <param name="min">Minimum value to set.</param>
/// <param name="max">Maximum value to set.</param>
/// <param name="required">This field will be required.</param>
public RequiredAttribute(float min, float max, bool required = true)
{
Min = (decimal)min;
Max = (decimal)max;
Required = required;
}
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
/// <param name="min">Minimum value to set.</param>
/// <param name="max">Maximum value to set.</param>
/// <param name="pattern">Pattern to check.</param>
/// <param name="required">This field will be required.</param>
public RequiredAttribute(float min, float max, string pattern, bool required = true)
{
Min = (decimal)min;
Max = (decimal)max;
Pattern = new Regex(pattern, RegexOptions.Compiled);
Required = required;
}
internal ValidateResult ValidateSimpleValue(DynamicPropertyInvoker dpi, object val)
{
if (val == null && Required)
return ValidateResult.ValueIsMissing;
if (dpi.Type.IsValueType)
{
if (val is decimal || val is long || val is int || val is float || val is double || val is short || val is byte ||
val is decimal? || val is long? || val is int? || val is float? || val is double? || val is short? || val is byte?)
{
decimal dec = Convert.ToDecimal(val);
if (Min.HasValue && Min.Value > dec)
return ValidateResult.ValueTooSmall;
if (Max.HasValue && Max.Value < dec)
return ValidateResult.ValueTooLarge;
return ValidateResult.Valid;
}
else
{
var str = val.ToString();
if (Min.HasValue && Min.Value > str.Length)
return ValidateResult.ValueTooShort;
if (Max.HasValue && Max.Value < str.Length)
return ValidateResult.ValueTooLong;
if (Pattern != null && !Pattern.IsMatch(str))
return ValidateResult.ValueDontMatchPattern;
return ValidateResult.Valid;
}
}
else if (dpi.Type.IsArray || dpi.IsGnericEnumerable)
{
var cnt = (val as IEnumerable<object>).Count();
if (Min.HasValue && Min.Value > cnt)
return ValidateResult.TooFewElementsInCollection;
if (Max.HasValue && Max.Value < cnt)
return ValidateResult.TooManyElementsInCollection;
return ValidateResult.Valid;
}
return ValidateResult.NotSupported;
}
}
}

View File

@@ -0,0 +1,36 @@
namespace DynamORM.Validation
{
/// <summary>Validation result enum.</summary>
public enum ValidateResult
{
/// <summary>The valid value.</summary>
Valid,
/// <summary>The value is missing.</summary>
ValueIsMissing,
/// <summary>The value too small.</summary>
ValueTooSmall,
/// <summary>The value too large.</summary>
ValueTooLarge,
/// <summary>The too few elements in collection.</summary>
TooFewElementsInCollection,
/// <summary>The too many elements in collection.</summary>
TooManyElementsInCollection,
/// <summary>The value too short.</summary>
ValueTooShort,
/// <summary>The value too long.</summary>
ValueTooLong,
/// <summary>The value don't match pattern.</summary>
ValueDontMatchPattern,
/// <summary>The not supported.</summary>
NotSupported,
}
}

View File

@@ -0,0 +1,17 @@
using DynamORM.Mapper;
namespace DynamORM.Validation
{
/// <summary>Validation result.</summary>
public class ValidationResult
{
/// <summary>Gets the property invoker.</summary>
public DynamicPropertyInvoker Property { get; internal set; }
/// <summary>Gets the requirement definition.</summary>
public RequiredAttribute Requirement { get; internal set; }
/// <summary>Gets the value that is broken.</summary>
public object Value { get; internal set; }
}
}