/// <summary> /// Merges an object into an instance of <see cref="QueryGroup"/> object. /// </summary> /// <typeparam name="TEntity">The type of the object.</typeparam> /// <param name="obj">The object to be merged.</param> /// <param name="queryGroup">The <see cref="QueryGroup"/> object to be merged.</param> /// <param name="dbSetting">The database setting that is currently in used.</param> /// <returns>An instance of converted dynamic object.</returns> internal static object Merge <TEntity>(this TEntity obj, QueryGroup queryGroup, IDbSetting dbSetting) where TEntity : class { return(Merge(obj, PropertyCache.Get <TEntity>(dbSetting).Select(p => p.PropertyInfo), queryGroup, dbSetting)); }
/// <summary> /// Returns the mapped property if the property is not present. /// </summary> /// <param name="type">The current type.</param> /// <param name="mappedName">The name of the property mapping.</param> /// <param name="dbSetting">The database setting that is currently in used.</param> /// <returns>The instance of <see cref="ClassProperty"/>.</returns> internal static ClassProperty GetPropertyByMapping(this Type type, string mappedName, IDbSetting dbSetting) { return(PropertyCache.Get(type, dbSetting) .FirstOrDefault(p => string.Equals(p.GetUnquotedMappedName(), mappedName, StringComparison.OrdinalIgnoreCase))); }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <param name="dictionary"></param> /// <param name="propertiesToSkip"></param> /// <param name="dbFields"></param> private static void CreateParameters(IDbCommand command, IDictionary <string, object> dictionary, IEnumerable <string> propertiesToSkip, IEnumerable <DbField> dbFields = null) { var kvps = dictionary.Where(kvp => propertiesToSkip?.Contains(kvp.Key, StringComparer.OrdinalIgnoreCase) != true); // Iterate the key value pairs foreach (var kvp in kvps) { var dbField = GetDbField(kvp.Key, dbFields); var value = kvp.Value; var classProperty = (ClassProperty)null; // CommandParameter if (kvp.Value is CommandParameter commandParameter) { value = commandParameter.Value; dbField = dbField ?? GetDbField(commandParameter.Field.Name, dbFields); classProperty = PropertyCache.Get(commandParameter.MappedToType, commandParameter.Field.Name); } command.Parameters.Add(CreateParameter(command, kvp.Key, value, classProperty, dbField, null, null)); } }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <param name="param"></param> /// <param name="propertiesToSkip"></param> /// <param name="dbFields"></param> private static void CreateParametersInternal(IDbCommand command, object param, IEnumerable <string> propertiesToSkip, IEnumerable <DbField> dbFields = null) { var type = param.GetType(); // Check if (type.IsGenericType && type.GetGenericTypeDefinition() == StaticType.Dictionary) { throw new InvalidParameterException("The supported type of dictionary object must be of type IDictionary<string, object>."); } // Variables var classProperties = type.IsClassType() ? PropertyCache.Get(type) : type.GetClassProperties(); // Skip if (propertiesToSkip != null) { classProperties = classProperties?.Where(p => propertiesToSkip.Contains(p.PropertyInfo.Name, StringComparer.OrdinalIgnoreCase) == false); } // Iterate foreach (var classProperty in classProperties) { var name = classProperty.GetMappedName(); var dbField = GetDbField(name, dbFields); var value = classProperty.PropertyInfo.GetValue(param); command.Parameters.Add(CreateParameter(command, name, value, classProperty, dbField, null, null)); } }
/// <summary> /// Creates a command parameter from the <see cref="QueryField"/> object. /// </summary> /// <param name="command">The command object to be used.</param> /// <param name="queryField">The value of <see cref="QueryField"/> object.</param> /// <param name="propertiesToSkip">The list of the properties to be skipped.</param> /// <param name="entityType">The type of the data entity.</param> internal static void CreateParameters(this IDbCommand command, QueryField queryField, IEnumerable <string> propertiesToSkip, Type entityType) { // Exclude those to be skipped if (propertiesToSkip?.Contains(queryField.Field.Name, StringComparer.OrdinalIgnoreCase) == true) { return; } // Get the values var value = queryField.Parameter.Value; var valueType = value?.GetType()?.GetUnderlyingType(); var dbType = (DbType?)null; #region PropertyHandler // Check the property handler var typeHandler = PropertyHandlerCache.Get <object>(valueType); if (typeHandler != null) { var classProperty = (ClassProperty)null; var setMethod = typeHandler.GetType().GetMethod("Set"); var returnType = setMethod.ReturnType; if (entityType != null) { classProperty = PropertyCache.Get(entityType, queryField.Field); } dbType = clientTypeToDbTypeResolver.Resolve(returnType); value = setMethod.Invoke(typeHandler, new[] { value, classProperty }); } #endregion #region DbType else { dbType = TypeMapCache.Get(valueType); // Check for the specialized types if (dbType == null) { if (valueType?.IsEnum == true) { dbType = DbType.String; } else if (valueType == StaticType.ByteArray) { dbType = DbType.Binary; } } } #endregion // Create the parameter command.Parameters.Add(command.CreateParameter(queryField.Parameter.Name, value, dbType)); }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <param name="queryField"></param> /// <param name="propertiesToSkip"></param> /// <param name="entityType"></param> /// <param name="dbFields"></param> private static void CreateParameters(this IDbCommand command, QueryField queryField, IEnumerable <string> propertiesToSkip, Type entityType, IEnumerable <DbField> dbFields = null) { if (queryField == null) { return; } var fieldName = queryField.Field.Name; // Skip if (propertiesToSkip?.Contains(fieldName, StringComparer.OrdinalIgnoreCase) == true) { return; } // Variables var dbField = GetDbField(fieldName, dbFields); var value = queryField.Parameter.Value; var classProperty = PropertyCache.Get(entityType, queryField.Field); var(direction, fallbackType) = queryField is DirectionalQueryField n ? ((ParameterDirection?)n.Direction, n.Type) : default;
/// <summary> /// /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entities"></param> /// <returns></returns> public static DataTable ToDataTable <TEntity>(string tableName, IEnumerable <TEntity> entities) where TEntity : class { var table = new DataTable() { TableName = tableName ?? ClassMappedNameCache.Get <TEntity>() }; var properties = PropertyCache.Get(entities?.First()?.GetType() ?? typeof(TEntity)); // Columns foreach (var property in properties) { table.Columns.Add(property.PropertyInfo.Name, property.PropertyInfo.PropertyType.GetUnderlyingType()); } // Rows foreach (var entity in entities) { var row = table.NewRow(); foreach (var property in properties) { var value = property.PropertyInfo.GetValue(entity); row[property.PropertyInfo.Name] = value == null ? DBNull.Value : value; } table.Rows.Add(row); } // Return return(table); }
/// <summary> /// Map the SortOrderField values from HotChocolate Custom Extensions to RepoDb specific /// OrderField(s) values that have the underlying DB field name (as potentially mapped on the Model). /// Null is returned if the value is undefined and/or invalid and cannot be mapped. /// NOTE: Property names and db fields names are not guaranteed to be the same. /// </summary> /// <returns></returns> public IEnumerable <OrderField> GetSortOrderFields() { var graphQLSortFields = this.GraphQLParamsContext?.SortArgs; //Ensure we are null safe and return null if no fields are specified... if (graphQLSortFields?.Any() != true) { return(null); } //NOTE: the RepDb PropertyCache provides mapping lookups, but only by mapped name (e.g. Database name) // for GraphQL (Pure Code First) we need to lookup the field by the Model's Property Name // and then convert to the mapped name. So we create a Lookup by Model Property Name! // For more info see: https://repodb.net/cacher/propertymappednamecache //TODO: Add Caching Layer here if needed to Cached a Reverse Dictionary of mappings by Model Name! var mappingLookup = PropertyCache.Get <TModel>().ToLookup(p => p.PropertyInfo.Name.ToLower()); var orderByFields = graphQLSortFields .Select(sf => new { //Null safe checking for the mapped field from RepoDb... //NOTE: We map based on the actual class property/member name not the fieldname which is // from the GraphQL schema and may be different than the underlying class property/member. RepoDbField = mappingLookup[sf.MemberName.ToLower()]?.FirstOrDefault()?.AsField(), //We test for Descending so that Ascending is always the default for a mismatch. RepoDbOrder = sf.IsDescending() ? Order.Descending : Order.Ascending }) //Filter out if the RepoDbField is null; meaning it's invalid and/or doesn't exist. .Where(f => f.RepoDbField != null) .Select(f => new OrderField(f.RepoDbField.Name, f.RepoDbOrder)); return(orderByFields); }
internal static IEnumerable <TEntity> AsEnumerable <TEntity>(this IDataReader reader) where TEntity : class { var properties = PropertyCache.Get <TEntity>(Command.None) .Where(property => property.PropertyInfo.CanWrite); var dictionary = new Dictionary <int, ClassProperty>(); for (var i = 0; i < reader.FieldCount; i++) { var property = properties.FirstOrDefault(p => p.GetMappedName().ToLower() == reader.GetName(i).ToLower()); if (property != null) { dictionary.Add(i, property); } } var list = new List <TEntity>(); while (reader.Read()) { var obj = Activator.CreateInstance <TEntity>(); foreach (var kvp in dictionary) { var value = reader.IsDBNull(kvp.Key) ? null : reader.GetValue(kvp.Key); kvp.Value.PropertyInfo.SetValue(obj, value); } list.Add(obj); } return(list); }
public void TestPropertyCacheGetForDerivedClass() { // Act var properties = PropertyCache.Get <DerivedClass>().AsList(); // Assert Assert.AreEqual <long>(4, properties.Count()); }
/// <summary> /// /// </summary> /// <param name="type"></param> /// <param name="dbFields"></param> /// <returns></returns> internal static Action <DbCommand, object> GetPlainTypeToDbParametersCompiledFunction(Type type, IEnumerable <DbField> dbFields = null) { var commandParameterExpression = Expression.Parameter(StaticType.DbCommand, "command"); var entityParameterExpression = Expression.Parameter(StaticType.Object, "entity"); var entityExpression = ConvertExpressionToTypeExpression(entityParameterExpression, type); var methodInfo = GetDbCommandCreateParameterMethod(); var callExpressions = new List <Expression>(); // Iterate foreach (var classProperty in PropertyCache.Get(type)) { var dbField = dbFields?.FirstOrDefault(df => string.Equals(df.Name, classProperty.GetMappedName(), StringComparison.OrdinalIgnoreCase)); var valueExpression = (Expression)Expression.Property(entityExpression, classProperty.PropertyInfo); // PropertyHandler valueExpression = ConvertExpressionToPropertyHandlerSetExpression(valueExpression, classProperty, classProperty.PropertyInfo.PropertyType); // Automatic if (Converter.ConversionType == ConversionType.Automatic && dbField?.Type != null) { valueExpression = ConvertExpressionWithAutomaticConversion(valueExpression, dbField.Type.GetUnderlyingType()); } // DbType var dbType = classProperty.GetDbType(); if (dbType == null && classProperty.PropertyInfo.PropertyType.IsEnum) { dbType = DbType.String; } var dbTypeExpression = dbType == null?GetNullableTypeExpression(StaticType.DbType) : ConvertExpressionToNullableExpression(Expression.Constant(dbType), StaticType.DbType); // DbCommandExtension.CreateParameter var expression = Expression.Call(methodInfo, new Expression[] { commandParameterExpression, Expression.Constant(classProperty.GetMappedName()), ConvertExpressionToTypeExpression(valueExpression, StaticType.Object), dbTypeExpression }); // DbCommand.Parameters.Add var parametersExpression = Expression.Property(commandParameterExpression, "Parameters"); var addExpression = Expression.Call(parametersExpression, GetDbParameterCollectionAddMethod(), expression); // Add callExpressions.Add(addExpression); } // Return return(Expression .Lambda <Action <DbCommand, object> >(Expression.Block(callExpressions), commandParameterExpression, entityParameterExpression) .Compile()); }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <param name="param"></param> /// <param name="propertiesToSkip"></param> /// <param name="dbFields"></param> private static void CreateParametersInternal(IDbCommand command, object param, IEnumerable <string> propertiesToSkip, IEnumerable <DbField> dbFields = null) { var type = param.GetType(); // Check if (type.IsGenericType && type.GetGenericTypeDefinition() == StaticType.Dictionary) { throw new InvalidParameterException("The supported type of dictionary object must be of type IDictionary<string, object>."); } // Variables var classProperties = type.IsClassType() ? PropertyCache.Get(type) : type.GetClassProperties(); // Skip if (propertiesToSkip != null) { classProperties = classProperties? .Where(p => propertiesToSkip?.Contains(p.PropertyInfo.Name, StringComparer.OrdinalIgnoreCase) == false); } // Iterate foreach (var classProperty in classProperties) { var name = classProperty.GetMappedName(); var dbField = GetDbField(name, dbFields); var value = classProperty.PropertyInfo.GetValue(param); var returnType = (Type)null; // Propertyhandler var propertyHandler = GetProperyHandler(classProperty, value?.GetType()); var definition = InvokePropertyHandlerSetMethod(propertyHandler, value, classProperty); if (definition != null) { returnType = definition.ReturnType.GetUnderlyingType(); value = definition.Value; } // Automatic if (IsAutomaticConversion(dbField)) { var underlyingType = dbField.Type.GetUnderlyingType(); value = AutomaticConvert(value, classProperty.PropertyInfo.PropertyType.GetUnderlyingType(), underlyingType); returnType = underlyingType; } // DbType var dbType = (returnType != null ? clientTypeToDbTypeResolver.Resolve(returnType) : null) ?? classProperty.GetDbType() ?? value?.GetType()?.GetDbType(); // Add the parameter command.Parameters.Add(command.CreateParameter(name, value, dbType)); } }
public void TestPropertyCacheForDerivedClassGetPropertyViaPropertyName() { // Act var actual = PropertyCache.Get <DerivedClass>("Property2"); var expected = "Property2"; // Assert Assert.AreEqual(expected, actual.GetMappedName()); }
public void TestPropertyCacheForBaseClassGetPropertyViaField() { // Act var actual = PropertyCache.Get <BaseClass>(new Field("Id")); var expected = "Id"; // Assert Assert.AreEqual(expected, actual.GetMappedName()); }
public void TestPropertyCacheForDerivedClass() { // Act var actual = PropertyCache.Get <DerivedClass>(); var expected = 4; // Assert Assert.AreEqual(expected, actual.Count()); }
public void TestPropertyCacheForDerivedClassGetMappedPropertyViaExpression() { // Act var actual = PropertyCache.Get <DerivedClass>(e => e.Property3); var expected = "Property4"; // Assert Assert.AreEqual(expected, actual.GetMappedName()); }
public void TestPropertyCacheForBaseClassGetPropertyViaExpression() { // Act var actual = PropertyCache.Get <BaseClass>(e => e.Id); var expected = "Id"; // Assert Assert.AreEqual(expected, actual.GetMappedName()); }
public void TestPropertyCacheForDerivedClassGetMappedPropertyViaField() { // Act var actual = PropertyCache.Get <DerivedClass>(new Field("Property4"), true); var expected = "Property4"; // Assert Assert.AreEqual(expected, actual.GetMappedName()); }
/// <summary> /// Gets a delegate that is used to convert the <see cref="DbDataReader"/> object into data entity object. /// </summary> /// <typeparam name="TEntity">The data entity object to convert to.</typeparam> /// <param name="reader">The <see cref="DbDataReader"/> to be converted.</param> /// <returns>An instance of data entity object.</returns> public static DataReaderToDataEntityDelegate <TEntity> GetDataReaderToDataEntityDelegate <TEntity>(DbDataReader reader) where TEntity : class { var entityType = typeof(TEntity); var dynamicMethod = new DynamicMethod(StringConstant.DynamicMethod, entityType, new[] { typeof(DbDataReader) }, typeof(Assembly).GetTypeInfo().Module, true); var ilGenerator = dynamicMethod.GetILGenerator(); var fieldDefinitions = FieldDefinitionCache.Get <TEntity>(Command.Query); // Declare IL Variables ilGenerator.DeclareLocal(entityType); ilGenerator.DeclareLocal(typeof(object)); // Create instance of the object type ilGenerator.Emit(OpCodes.Newobj, entityType.GetTypeInfo().GetConstructor(Type.EmptyTypes)); ilGenerator.Emit(OpCodes.Stloc, 0); // Matching the fields var fields = Enumerable.Range(0, reader.FieldCount) .Select(reader.GetName) .Select(n => n.ToLower()) .ToList(); var matchedCount = 0; // Iterate the properties PropertyCache.Get <TEntity>(Command.Query) .Where(property => property.PropertyInfo.CanWrite) .ToList() .ForEach(property => { var mappedName = property.GetMappedName().ToLower(); var fieldDefinition = fieldDefinitions?.FirstOrDefault(fd => fd.Name.ToLower() == mappedName); var ordinal = fields.IndexOf(mappedName); if (ordinal >= 0) { EmitDataReaderToDataEntityMapping <TEntity>(ilGenerator, ordinal, property, fieldDefinition); matchedCount++; } }); // Throw an error if there are no matching atleast one if (matchedCount == 0) { throw new NoMatchedFieldsException($"There is no matching fields between the result set of the data reader and the type '{typeof(TEntity).FullName}'."); } // Return the TEntity instance value ilGenerator.Emit(OpCodes.Ldloc, 0); ilGenerator.Emit(OpCodes.Ret); // Create a delegate return((DataReaderToDataEntityDelegate <TEntity>)dynamicMethod.CreateDelegate(typeof(DataReaderToDataEntityDelegate <TEntity>))); }
/// <summary> /// Gets the list of enumerable <see cref="ClassProperty"/> objects of the type. /// </summary> /// <param name="type">The current type.</param> /// <returns>The list of the enumerable <see cref="ClassProperty"/> objects.</returns> internal static IEnumerable <ClassProperty> GetEnumerableClassProperties(this Type type) => PropertyCache.Get(type).Where(classProperty => { var propType = classProperty.PropertyInfo.PropertyType; return (propType != StaticType.String && propType != StaticType.CharArray && propType != StaticType.ByteArray && StaticType.IEnumerable.IsAssignableFrom(propType)); });
public void TestWithMapAttribute() { // Act var property = PropertyCache.Get <PropertyMappedNameCacheTestClass>() .First(p => p.PropertyInfo.Name == "PropertyString"); var expected = "PropertyName"; // Assert Assert.AreEqual(expected, property.GetMappedName()); }
/// <summary> /// /// </summary> /// <param name="command"></param> /// <param name="dictionary"></param> /// <param name="propertiesToSkip"></param> /// <param name="dbFields"></param> private static void CreateParameters(IDbCommand command, IDictionary <string, object> dictionary, IEnumerable <string> propertiesToSkip, IEnumerable <DbField> dbFields = null) { var kvps = dictionary.Where(kvp => propertiesToSkip?.Contains(kvp.Key, StringComparer.OrdinalIgnoreCase) != true); // Iterate the key value pairs foreach (var kvp in kvps) { var dbField = GetDbField(kvp.Key, dbFields); var value = kvp.Value; var classProperty = (ClassProperty)null; var valueType = (Type)null; // CommandParameter if (kvp.Value is CommandParameter) { var commandParameter = (CommandParameter)kvp.Value; classProperty = PropertyCache.Get(commandParameter.MappedToType, kvp.Key); value = commandParameter.Value; valueType = value?.GetType()?.GetUnderlyingType(); } else { valueType = kvp.Value?.GetType()?.GetUnderlyingType(); } // Propertyhandler var propertyHandler = GetProperyHandler(classProperty, valueType); var definition = InvokePropertyHandlerSetMethod(propertyHandler, value, classProperty); if (definition != null) { valueType = definition.ReturnType.GetUnderlyingType(); value = definition.Value; } // Automatic if (IsAutomaticConversion(dbField)) { var dbFieldType = dbField.Type.GetUnderlyingType(); value = AutomaticConvert(value, valueType, dbFieldType); valueType = dbFieldType; } // DbType var dbType = (valueType != null ? clientTypeToDbTypeResolver.Resolve(valueType) : null) ?? classProperty?.GetDbType() ?? value?.GetType()?.GetDbType(); // Add the parameter command.Parameters.Add(command.CreateParameter(kvp.Key, value, dbType)); } }
public void TestTypeMapAttribute() { // Act var actual = PropertyCache.Get <TypeMapAttributeTestClass>() .First(p => p.PropertyInfo.Name == "ColumnDateTime"); var result = actual.GetDbType(); var expected = DbType.DateTime2; // Assert Assert.AreEqual(expected, result); }
public void TestPropertyValueAttributeResolverWithMappedAttributesAndWithIncludeMappingsFalseViaPropertyInfo() { // Prepare var classProperty = PropertyCache.Get <PropertyValueAttributeClass>("PropertyString", true); // Act var actual = new PropertyValueAttributeResolver().Resolve(classProperty.PropertyInfo, false); // Assert Assert.AreEqual(0, actual.Count()); }
public void TestPropertyValueAttributeResolverCollisionsWithIncludeMappingsFalseViaPropertyInfo() { // Prepare var classProperty = PropertyCache.Get <PropertyValueAttributeClass>("PropertyDecimal", true); // Act var actual = new PropertyValueAttributeResolver().Resolve(classProperty.PropertyInfo, false); // Assert Assert.AreEqual(7, actual.Count()); }
public void TestPropertyValueAttributeResolverWithAttributesViaPropertyInfo() { // Prepare var classProperty = PropertyCache.Get <PropertyValueAttributeClass>("PropertyInt", true); // Act var actual = new PropertyValueAttributeResolver().Resolve(classProperty.PropertyInfo); // Assert Assert.AreEqual(2, actual.Count()); }
public void TestPropertyInfoGetPropertyValueAttributesMethod() { // Prepare var property = PropertyCache.Get <PropertyValueAttributeClass>(e => e.PropertyString); // Act var attributes = property.PropertyInfo.GetPropertyValueAttributes(false); // Assert Assert.AreEqual(7, attributes.Count()); }