/// <summary> /// Gets the changed values. /// </summary> /// <param name="clientEntity">The client entity.</param> /// <param name="entity">The entity.</param> /// <param name="entityType">Type of the entity.</param> /// <returns></returns> public static IEnumerable<KeyValuePair<string, object>> GetGeneratedValues(object clientEntity, object entity, EntityType entityType) { if (clientEntity == null) throw new ArgumentNullException("clientEntity"); if (entity == null) throw new ArgumentNullException("entity"); if (entityType == null) throw new ArgumentNullException("entityType"); if (entity.GetType() != clientEntity.GetType()) throw new BeetleException(Resources.EntityAndClientEntityMustBeSameTypeToCompare); var retVal = new List<KeyValuePair<string, object>>(); // detect changed values after the client post. foreach (var property in entityType.AllDataProperties) { var oldValue = GetPropertyValue(clientEntity, property.Name); var newValue = GetPropertyValue(entity, property.Name); if (!Equals(oldValue, newValue)) retVal.Add(new KeyValuePair<string, object>(property.Name, newValue)); } foreach (var complexProperty in entityType.AllComplexProperties) { var oldValue = GetPropertyValue(clientEntity, complexProperty.Name); var newValue = GetPropertyValue(entity, complexProperty.Name); var generatedValues = GetGeneratedValues(oldValue, newValue, complexProperty.ComplexType); foreach (var generatedValue in generatedValues) retVal.Add(new KeyValuePair<string, object>(complexProperty.Name + "." + generatedValue.Key, generatedValue.Value)); } return retVal; }
/// <summary> /// Gets the metadata. /// </summary> /// <param name="connection">The connection.</param> /// <param name="modelNamespace">The model namespace.</param> /// <param name="modelAssemblyName">Name of the model assembly.</param> /// <returns></returns> public static Metadata GetMetadata(IDbConnection connection, string modelNamespace = null, string modelAssemblyName = null) { var metadata = new Metadata(connection.Database); bool closeConnection; if (connection.State == ConnectionState.Closed) { connection.Open(); closeConnection = true; } else closeConnection = false; using (var tablesCommand = connection.CreateCommand()) { const string schemaSql = @" select distinct t.TABLE_NAME, c.COLUMN_NAME, c.COLUMN_DEFAULT, c.IS_NULLABLE, c.DATA_TYPE, c.CHARACTER_MAXIMUM_LENGTH, c.NUMERIC_PRECISION, c.NUMERIC_SCALE, case when COALESCE(ck.COLUMN_NAME, '') = '' then 0 else 1 end as IsKey, {0} as GenerationPattern from INFORMATION_SCHEMA.TABLES t inner join INFORMATION_SCHEMA.COLUMNS c on t.TABLE_NAME = c.TABLE_NAME left join INFORMATION_SCHEMA.TABLE_CONSTRAINTS tk on t.TABLE_NAME = tk.TABLE_NAME and tk.CONSTRAINT_TYPE = 'PRIMARY KEY' left join INFORMATION_SCHEMA.KEY_COLUMN_USAGE ck on t.TABLE_NAME = ck.TABLE_NAME and c.COLUMN_NAME = ck.COLUMN_NAME order by t.TABLE_NAME, c.COLUMN_NAME "; const string sqlServerGenerationPatternSql = "columnproperty(object_id(t.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity')+(2*columnproperty(object_id(t.TABLE_NAME), c.COLUMN_NAME, 'IsComputed'))"; const string sqlCeGenerationPatternSql = "case when AUTOINC_NEXT > 0 then 1 else 0 end"; const string mySqlGenerationPatternSql = "INSTR(EXTRA, 'auto_increment')"; string generationPatternSql; // not the ideal solution but I couldn't find a better way var connectionType = connection.GetType().ToString(); if (connectionType.Contains("SqlConnection")) generationPatternSql = sqlServerGenerationPatternSql; else if (connectionType.Contains("SqlCe")) generationPatternSql = sqlCeGenerationPatternSql; else if (connectionType.Contains("MySql") || connectionType.Contains("Oracle")) generationPatternSql = mySqlGenerationPatternSql; else generationPatternSql = "0"; tablesCommand.CommandText = string.Format(schemaSql, generationPatternSql); using (var tablesReader = tablesCommand.ExecuteReader()) { EntityType entityType = null; Type clrType = null; while (tablesReader.Read()) { var tableName = tablesReader.GetString(0); var columnName = tablesReader.GetString(1); var defaultValue = tablesReader.GetValue(2); var isNullable = tablesReader.GetString(3) == "YES"; var dataType = tablesReader.GetString(4); var maxLength = tablesReader.GetValue(5); var precision = tablesReader.GetValue(6); var scale = tablesReader.GetValue(7); var isKey = tablesReader.GetInt32(8) > 0; var generationPattern = tablesReader.GetInt32(9); if (entityType == null || entityType.ShortName != tableName) { var fullName = tableName; if (!string.IsNullOrEmpty(modelNamespace)) fullName = modelNamespace + "." + fullName; if (!string.IsNullOrEmpty(modelAssemblyName)) fullName += ", " + modelAssemblyName; // we use fullName to create instance of model class entityType = new EntityType(fullName, tableName) { QueryName = Pluralize(tableName) }; clrType = Type.GetType(entityType.Name); entityType.ClrType = clrType; metadata.Entities.Add(entityType); } var dataTypeEnum = GetDataType(dataType); if (defaultValue != null) { var defaultStr = defaultValue.ToString(); // todo: check if MySql, Oracle etc returns default value in parenthesis if (defaultStr.StartsWith("((")) defaultStr = defaultStr.Substring(2, defaultStr.Length - 4); else if (defaultStr.StartsWith("(")) defaultStr = defaultStr.Substring(1, defaultStr.Length - 2); defaultValue = defaultStr; if (dataTypeEnum == DataType.Boolean) defaultValue = defaultStr == "1"; } Func<string> displayNameGetter = null; if (clrType != null) { var propertyInfo = clrType.GetMember(columnName).FirstOrDefault(); if (propertyInfo != null) displayNameGetter = GetDisplayNameGetter(propertyInfo); } var dataProperty = new DataProperty(columnName, displayNameGetter) { ResourceName = columnName, DataType = dataTypeEnum, DefaultValue = defaultValue == DBNull.Value ? null : defaultValue, EnumType = null, GenerationPattern = (GenerationPattern)generationPattern, // we cannot read this from INFORMATION_SCHEMA IsEnum = false, IsNullable = isNullable, Precision = precision == DBNull.Value ? (int?)null : Convert.ToInt32(precision), Scale = scale == DBNull.Value ? (int?)null : Convert.ToInt32(scale) }; var iMaxLength = maxLength == DBNull.Value ? (int?)null : (int)maxLength; if (!isNullable) dataProperty.Validators.Add(Meta.Validator.Required(string.Format(Resources.RequiredError, columnName), "requiredError", false)); if (iMaxLength.HasValue) dataProperty.Validators.Add(Meta.Validator.StringLength(string.Format(Resources.MaxLenError, columnName), "maxLenError", 0, iMaxLength.Value)); if (isKey) entityType.Keys.Add(columnName); if (clrType != null) PopulateDataPropertyValidations(clrType, dataProperty, iMaxLength); entityType.DataProperties.Add(dataProperty); } } } using (var navigationCommand = connection.CreateCommand()) { navigationCommand.CommandText = @" select distinct FK.TABLE_NAME, CU.COLUMN_NAME, case when coalesce(CU2.COLUMN_NAME, '') = '' then 0 else 1 end as IsOneToOne, PK.TABLE_NAME, C.CONSTRAINT_NAME, c.DELETE_RULE from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK on C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK on C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU on C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME left join INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK2 on FK2.TABLE_NAME = FK.TABLE_NAME and FK2.CONSTRAINT_TYPE = 'PRIMARY KEY' left join INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU2 on FK2.CONSTRAINT_NAME = CU2.CONSTRAINT_NAME and CU.COLUMN_NAME = CU2.COLUMN_NAME "; using (var navigationReader = navigationCommand.ExecuteReader()) { while (navigationReader.Read()) { var fkTable = navigationReader.GetString(0); var fkColumn = navigationReader.GetString(1); var isOneToOne = navigationReader.GetInt32(2) > 0; var pkTable = navigationReader.GetString(3); var constraintName = navigationReader.GetString(4); var cascadeDelete = navigationReader.GetString(5) == "CASCADE"; var fkEntity = metadata.Entities.First(e => e.ShortName == fkTable); var fkNavigation = fkEntity.NavigationProperties.FirstOrDefault(np => np.AssociationName == constraintName); if (fkNavigation == null) { Func<string> displayNameGetter = null; if (fkEntity.ClrType != null) { var propertyInfo = fkEntity.ClrType.GetMember(fkNavigation.Name).FirstOrDefault(); if (propertyInfo != null) displayNameGetter = GetDisplayNameGetter(propertyInfo); } fkNavigation = new NavigationProperty(pkTable, displayNameGetter) { ResourceName = pkTable, AssociationName = constraintName, DoCascadeDelete = false, EntityTypeName = pkTable, IsScalar = true }; fkEntity.NavigationProperties.Add(fkNavigation); } fkNavigation.ForeignKeys.Add(fkColumn); if (fkEntity.ClrType != null) PopulateNavigationPropertyValidations(fkEntity.ClrType, fkNavigation); var pkEntity = metadata.Entities.First(e => e.ShortName == pkTable); var pkNavigation = pkEntity.NavigationProperties.FirstOrDefault(np => np.AssociationName == constraintName); if (pkNavigation == null) { Func<string> displayNameGetter = null; if (pkEntity.ClrType != null) { var propertyInfo = pkEntity.ClrType.GetMember(pkNavigation.Name).FirstOrDefault(); if (propertyInfo != null) displayNameGetter = GetDisplayNameGetter(propertyInfo); } var pkNavName = isOneToOne ? fkTable : Pluralize(fkTable); pkNavigation = new NavigationProperty(pkNavName, displayNameGetter) { ResourceName = pkNavName, AssociationName = constraintName, DoCascadeDelete = cascadeDelete, EntityTypeName = fkTable, IsScalar = isOneToOne }; pkEntity.NavigationProperties.Add(pkNavigation); } else pkNavigation.IsScalar &= isOneToOne; if (pkEntity.ClrType != null) PopulateNavigationPropertyValidations(pkEntity.ClrType, pkNavigation); } } } if (closeConnection) connection.Close(); metadata.FixReferences(); return metadata; }
/// <summary> /// Initializes a new instance of the <see cref="EntityBag" /> class. /// </summary> /// <param name="clientEntity">The client entity.</param> /// <param name="entity">The entity.</param> /// <param name="entityState">State of the entity.</param> /// <param name="originalValues">The original values.</param> /// <param name="index">The index.</param> /// <param name="entityType">Type of the entity.</param> /// <param name="forceUpdate">The force update.</param> public EntityBag(object clientEntity, object entity, EntityState entityState, IDictionary<string, object> originalValues, int index, EntityType entityType, bool? forceUpdate = null) : this(clientEntity, entity, entityState, originalValues, index, forceUpdate) { EntityType = entityType; }