diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 8bd0c6b..37db94a 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -48,12 +48,14 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Text; +using System.Text.RegularExpressions; using DynamORM.Builders; using DynamORM.Builders.Extensions; using DynamORM.Builders.Implementation; using DynamORM.Helpers; using DynamORM.Helpers.Dynamics; using DynamORM.Mapper; +using DynamORM.Validation; [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "This is a generated file which generates all the necessary support classes.")] [module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1403:FileMayOnlyContainASingleNamespace", Justification = "This is a generated file which generates all the necessary support classes.")] @@ -2857,6 +2859,7 @@ namespace DynamORM { case "varchar": return DbType.AnsiString; + case "nvarchar": return DbType.String; } @@ -5066,7 +5069,7 @@ namespace DynamORM return defaultValue; } - /// Delegate fro try parse function of a type. + /// Delegate from try parse function of a type. /// Type which implements this function. /// Value to parse. /// Resulting value. @@ -6510,7 +6513,7 @@ namespace DynamORM namespace Builders { /// Dynamic delete query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. @@ -6556,7 +6559,7 @@ namespace DynamORM } /// Dynamic insert query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicInsertQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. @@ -6587,7 +6590,7 @@ namespace DynamORM } /// Dynamic query builder base interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicQueryBuilder : IExtendedDisposable { /// Gets instance. @@ -6627,7 +6630,7 @@ namespace DynamORM } /// Dynamic select query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicSelectQueryBuilder : IDynamicQueryBuilder ////, IEnumerable { /// Execute this builder. @@ -6881,7 +6884,7 @@ namespace DynamORM } /// Dynamic update query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. @@ -12420,7 +12423,15 @@ namespace DynamORM throw new ArgumentException(string.Format("Argument '{0}' must be dynamic.", p.Name)); } - _uncertainResult = f.DynamicInvoke(_arguments.ToArray()); + try + { + _uncertainResult = f.DynamicInvoke(_arguments.ToArray()); + } + catch (TargetInvocationException e) + { + if (e.InnerException != null) throw e.InnerException; + else throw e; + } } /// @@ -13097,8 +13108,11 @@ namespace DynamORM public int Ordinal { get; set; } } - private Type _arrayType; - private bool _genericEnumerable; + /// Gets the array type of property if main type is a form of collection. + public Type ArrayType { get; private set; } + + /// Gets a value indicating whether this property is in fact a generic list. + public bool IsGnericEnumerable { get; private set; } /// Gets the type of property. public Type Type { get; private set; } @@ -13115,6 +13129,9 @@ namespace DynamORM /// Gets type column description. public ColumnAttribute Column { get; private set; } + /// Gets type list of property requirements. + public List Requirements { get; private set; } + /// Gets a value indicating whether this is ignored in some cases. public bool Ignore { get; private set; } @@ -13130,21 +13147,22 @@ namespace DynamORM Type = property.PropertyType; object[] ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false); + Requirements = property.GetCustomAttributes(typeof(RequiredAttribute), false).Cast().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; @@ -13199,20 +13217,20 @@ namespace DynamORM { if (!Type.IsAssignableFrom(val.GetType())) { - if (Type.IsArray || _genericEnumerable) + if (Type.IsArray || IsGnericEnumerable) { if (val != null) { - var lst = (val as IEnumerable).Select(x => GetElementVal(_arrayType, x)).ToList(); + var lst = (val as IEnumerable).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); @@ -13248,7 +13266,17 @@ namespace DynamORM 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)) @@ -13259,7 +13287,17 @@ namespace DynamORM 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 @@ -13377,6 +13415,57 @@ namespace DynamORM return destination; } + /// Validates the object. + /// The value. + /// List of not valid results. + public IList ValidateObject(object val) + { + var result = new List(); + + 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) + 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 GetAllMembers(Type type) { if (type.IsInterface) @@ -13451,4 +13540,167 @@ namespace DynamORM public bool Override { get; set; } } } + + namespace Validation + { + /// Required attribute can be used to validate fields in objects using mapper class. + [AttributeUsage(AttributeTargets.Property)] + public class RequiredAttribute : Attribute + { + /// Gets or sets minimum value or length of field. + public decimal? Min { get; set; } + + /// Gets or sets maximum value or length of field. + public decimal? Max { get; set; } + + /// Gets or sets pattern to verify. + public Regex Pattern { get; set; } + + /// Gets or sets a value indicating whether property value is required or not. + public bool Required { get; set; } + + /// Initializes a new instance of the class. + /// This field will be required. + public RequiredAttribute(bool required = true) + { + Required = required; + } + + /// Initializes a new instance of the class. + /// Limiting value to set. + /// Whether set maximum parameter (true) or minimum parameter (false). + /// This field will be required. + public RequiredAttribute(float val, bool max, bool required = true) + { + if (max) + Max = (decimal)val; + else + Min = (decimal)val; + Required = required; + } + + /// Initializes a new instance of the class. + /// Minimum value to set. + /// Maximum value to set. + /// This field will be required. + public RequiredAttribute(float min, float max, bool required = true) + { + Min = (decimal)min; + Max = (decimal)max; + Required = required; + } + + /// Initializes a new instance of the class. + /// Minimum value to set. + /// Maximum value to set. + /// Pattern to check. + /// This field will be required. + 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).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; + } + } + + /// Validation result enum. + public enum ValidateResult + { + /// The valid value. + Valid, + + /// The value is missing. + ValueIsMissing, + + /// The value too small. + ValueTooSmall, + + /// The value too large. + ValueTooLarge, + + /// The too few elements in collection. + TooFewElementsInCollection, + + /// The too many elements in collection. + TooManyElementsInCollection, + + /// The value too short. + ValueTooShort, + + /// The value too long. + ValueTooLong, + + /// The value don't match pattern. + ValueDontMatchPattern, + + /// The not supported. + NotSupported, + } + + /// Validation result. + public class ValidationResult + { + /// Gets the property invoker. + public DynamicPropertyInvoker Property { get; internal set; } + + /// Gets the requirement definition. + public RequiredAttribute Requirement { get; internal set; } + + /// Gets the value that is broken. + public object Value { get; internal set; } + } + } } \ No newline at end of file diff --git a/DynamORM/Builders/IDynamicDeleteQueryBuilder.cs b/DynamORM/Builders/IDynamicDeleteQueryBuilder.cs index 17629aa..915e2cb 100644 --- a/DynamORM/Builders/IDynamicDeleteQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicDeleteQueryBuilder.cs @@ -31,7 +31,7 @@ using System; namespace DynamORM.Builders { /// Dynamic delete query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. diff --git a/DynamORM/Builders/IDynamicInsertQueryBuilder.cs b/DynamORM/Builders/IDynamicInsertQueryBuilder.cs index e7e2cde..866f9e7 100644 --- a/DynamORM/Builders/IDynamicInsertQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicInsertQueryBuilder.cs @@ -31,7 +31,7 @@ using System; namespace DynamORM.Builders { /// Dynamic insert query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicInsertQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. diff --git a/DynamORM/Builders/IDynamicQueryBuilder.cs b/DynamORM/Builders/IDynamicQueryBuilder.cs index 62b2554..4366bb3 100644 --- a/DynamORM/Builders/IDynamicQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicQueryBuilder.cs @@ -34,7 +34,7 @@ using DynamORM.Helpers; namespace DynamORM.Builders { /// Dynamic query builder base interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicQueryBuilder : IExtendedDisposable { /// Gets instance. diff --git a/DynamORM/Builders/IDynamicSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicSelectQueryBuilder.cs index faabac2..4ca0448 100644 --- a/DynamORM/Builders/IDynamicSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicSelectQueryBuilder.cs @@ -33,7 +33,7 @@ using System.Data; namespace DynamORM.Builders { /// Dynamic select query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicSelectQueryBuilder : IDynamicQueryBuilder ////, IEnumerable { /// Execute this builder. diff --git a/DynamORM/Builders/IDynamicUpdateQueryBuilder.cs b/DynamORM/Builders/IDynamicUpdateQueryBuilder.cs index 5f83381..5938a47 100644 --- a/DynamORM/Builders/IDynamicUpdateQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicUpdateQueryBuilder.cs @@ -31,7 +31,7 @@ using System; namespace DynamORM.Builders { /// Dynamic update query builder interface. - /// This interface it publically available. Implementation should be hidden. + /// This interface it publicly available. Implementation should be hidden. public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. diff --git a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs index 012a0fb..4b4897c 100644 --- a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs @@ -227,7 +227,7 @@ namespace DynamORM.Builders.Implementation reader(rdr); } - /// Execute this builder as a data reader, but + /// Execute this builder as a data reader, but /// first makes a full reader copy in memory. /// Action containing reader. public virtual void ExecuteCachedDataReader(Action reader) diff --git a/DynamORM/DynamORM.csproj b/DynamORM/DynamORM.csproj index cf107bc..e4773c5 100644 --- a/DynamORM/DynamORM.csproj +++ b/DynamORM/DynamORM.csproj @@ -104,11 +104,14 @@ + + +