예제 #1
0
        /// <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;
        }
예제 #2
0
        /// <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;
        }
예제 #3
0
 /// <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;
 }