Beispiel #1
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;
        }
        /// <summary>
        /// Generates mappings.
        /// </summary>
        /// <param name="entityResources">The entity resources.</param>
        /// <param name="enumResources">The enum resources.</param>
        /// <param name="itemCollection">The item collection.</param>
        /// <param name="container">The entity container.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">itemCollection</exception>
        /// <exception cref="System.Exception">Unknown data type:  + p.TypeUsage.EdmType.Name</exception>
        private static Metadata Mapping(IEnumerable<EntityResource> entityResources, IEnumerable<EnumResource> enumResources, IEnumerable<GlobalItem> itemCollection, EntityContainer container) {
            if (itemCollection == null)
                throw new ArgumentNullException("itemCollection");

            var globalItems = itemCollection as IList<GlobalItem> ?? itemCollection.ToList();
            var retVal = new Metadata(container.Name);

            // entity types
            foreach (var er in entityResources) {
                var fullName = string.Format("{0}, {1}", er.ClrType.FullName, er.ClrType.Assembly.GetName().Name);
                var et = new Meta.EntityType(fullName, er.Name) {TableName = er.TableName};

                // entity informations
                if (er.Entity != null) {
                    et.QueryName = er.EntitySet.Name;
                    if (er.EntitySet.ElementType.Name != er.Name)
                        et.QueryType = er.EntitySet.ElementType.Name;
                    et.Keys.AddRange(er.Entity.KeyMembers.Select(k => k.Name));
                    // if entity has base type, set the base type's name
                    if (er.Entity.BaseType != null)
                        et.BaseTypeName = er.Entity.BaseType.Name;
                    if (er.ClrType != null)
                        et.ClrType = er.ClrType;

                    // navigation properties
                    if (er.NavigationProperties != null) {
                        foreach (var p in er.NavigationProperties) {
                            var ass = globalItems.OfType<AssociationType>().First(a => a.Name == p.RelationshipType.Name);
                            Func<string> displayNameGetter = null;
                            if (er.ClrType != null) {
                                var propertyInfo = er.ClrType.GetMember(p.Name).FirstOrDefault();
                                if (propertyInfo != null)
                                    displayNameGetter = Helper.GetDisplayNameGetter(propertyInfo);
                            }

                            var np = new Meta.NavigationProperty(p.Name, displayNameGetter);
                            np.EntityTypeName = (((RefType)p.ToEndMember.TypeUsage.EdmType).ElementType).Name;

                            var isScalar = p.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many;
                            if (isScalar) {
                                np.IsScalar = true;
                                np.ForeignKeys.AddRange(ass.ReferentialConstraints.SelectMany(rc => rc.ToProperties.Select(tp => tp.Name)));
                            }

                            if (p.FromEndMember.DeleteBehavior == OperationAction.Cascade)
                                np.DoCascadeDelete = true;

                            if (er.ClrType != null)
                                Helper.PopulateNavigationPropertyValidations(er.ClrType, np);

                            np.AssociationName = p.RelationshipType.Name;

                            et.NavigationProperties.Add(np);
                        }
                    }

                    // complex properties
                    if (er.ComplexProperties != null) {
                        foreach (var p in er.ComplexProperties) {
                            Func<string> displayNameGetter = null;
                            if (er.ClrType != null) {
                                var propertyInfo = er.ClrType.GetMember(p.Key.Name).FirstOrDefault();
                                if (propertyInfo != null)
                                    displayNameGetter = Helper.GetDisplayNameGetter(propertyInfo);
                            }

                            var cp = new ComplexProperty(p.Key.Name, displayNameGetter) {
                                TypeName = p.Key.TypeUsage.EdmType.Name,
                                Mappings = p.Value
                            };

                            et.ComplexProperties.Add(cp);
                        }
                    }
                }
                else
                    et.IsComplexType = true; // this is a complex type

                // data properties
                foreach (var sp in er.SimpleProperties) {
                    var p = sp.Value;
                    var clrType = UnderlyingClrType(p.TypeUsage.EdmType);
                    Func<string> displayNameGetter = null;
                    if (er.ClrType != null) {
                        var propertyInfo = clrType.GetMember(p.Name).FirstOrDefault();
                        if (propertyInfo != null)
                            displayNameGetter = Helper.GetDisplayNameGetter(propertyInfo);
                    }

                    var dp = new DataProperty(p.Name, displayNameGetter) { ColumnName = sp.Key };

                    var jsType = DataType.Binary;
                    var enumType = p.TypeUsage.EdmType as EnumType;
                    if (enumType != null) {
                        dp.IsEnum = true;
                        dp.EnumType = enumType.Name;
                    }
                    else {
                        // convert CLR type to javascript type
                        if (clrType == typeof(string))
                            jsType = DataType.String;
                        else if (clrType == typeof(Guid))
                            jsType = DataType.Guid;
                        else if (clrType == typeof(DateTime))
                            jsType = DataType.Date;
                        else if (clrType == typeof(DateTimeOffset))
                            jsType = DataType.DateTimeOffset;
                        else if (clrType == typeof(TimeSpan))
                            jsType = DataType.Time;
                        else if (clrType == typeof(bool))
                            jsType = DataType.Boolean;
                        else if (clrType == typeof(Int16) || clrType == typeof(Int64) || clrType == typeof(Int32))
                            jsType = DataType.Int;
                        else if (clrType == typeof(Single) || clrType == typeof(double) || clrType == typeof(decimal))
                            jsType = DataType.Number;
                        else if (clrType == typeof(byte))
                            jsType = DataType.Byte;
                        else if (clrType == typeof(DbGeography))
                            jsType = DataType.Geography;
                        else if (clrType == typeof(DbGeometry))
                            jsType = DataType.Geometry;
                        else if (clrType == typeof(byte[]))
                            jsType = DataType.Binary;
                        else throw new BeetleException(Resources.UnknownDataType + p.TypeUsage.EdmType.Name);
                    }
                    dp.DataType = jsType;
                    var generated = p.MetadataProperties.FirstOrDefault(m => m.Name == StoreGeneratedPatternAttributeName);
                    if (generated == null)
                        dp.GenerationPattern = GenerationPattern.None;
                    else if (generated.Value.ToString().StartsWith("I"))
                        dp.GenerationPattern = GenerationPattern.Identity;
                    else
                        dp.GenerationPattern = GenerationPattern.Computed;
                    dp.UseForConcurrency = IsConcurrencyProperty(p);
                    if (p.Nullable)
                        dp.IsNullable = true;
                    if (jsType == DataType.Number) {
                        var precision = GetPrecision(p);
                        var scale = GetScale(p);
                        if (precision.HasValue)
                            dp.Precision = precision;
                        if (scale.HasValue)
                            dp.Scale = scale;
                    }

                    if (er.ClrType != null)
                        Helper.PopulateDataPropertyValidations(er.ClrType, dp, GetMaxStringLength(p));
                    if (p.DefaultValue != null)
                        dp.DefaultValue = p.DefaultValue;

                    et.DataProperties.Add(dp);
                }
                retVal.Entities.Add(et);
            }

            // enum types
            foreach (var enumResource in enumResources) {
                var enumType = enumResource.EnumType;
                //var enumClrType = enumResource.ClrType;
                var et = new Meta.EnumType(enumType.Name);

                foreach (var member in enumType.Members) {
                    //todo: enum member için display
                    var em = new Meta.EnumMember(member.Name, null) {Value = member.Value};

                    et.Members.Add(em);
                }
                retVal.Enums.Add(et);
            }

            retVal.FixReferences();
            return retVal;
        }
Beispiel #3
0
        /// <summary>
        /// Gets the generated values comparing Entity-ClientEntity for each EntityBag.
        /// </summary>
        /// <param name="entityBags">The entity bags.</param>
        /// <param name="metadata">The metadata.</param>
        /// <returns>
        /// Generated-modified values for EntityBags.
        /// </returns>
        public static IEnumerable<GeneratedValue> GetGeneratedValues(IEnumerable<EntityBag> entityBags, Metadata metadata) {
            var generatedValues = new List<Tuple<GeneratedValue, bool>>();
            // populate auto-generated values
            foreach (var entityBagTmp in entityBags.Where(el => el.EntityState == EntityState.Added || el.EntityState == EntityState.Modified)) {
                var entityBag = entityBagTmp;
                var entityType = entityBag.EntityType;
                if (entityType == null) {
                    if (metadata == null)
                        throw new BeetleException(Resources.CannotGetGeneratedValues);

                    var type = entityBag.Entity.GetType();
                    var entityTypeName = string.Format("{0}, {1}", type.FullName, type.Assembly.GetName().Name);
                    entityType = metadata.Entities.FirstOrDefault(e => e.Name == entityTypeName);
                    if (entityType == null)
                        throw new BeetleException(string.Format(Resources.CannotFindMetadata, entityTypeName));
                }

                var changedValues = GetGeneratedValues(entityBag.ClientEntity, entityBag.Entity, entityType);
                if (changedValues != null) {
                    foreach (var changedValue in changedValues) {
                        var isKeyPart = entityType.Keys.Contains(changedValue.Key);
                        generatedValues.Add(new Tuple<GeneratedValue, bool>(new GeneratedValue(entityBag.Index, changedValue.Key, changedValue.Value), isKeyPart));
                    }
                }
            }

            return generatedValues.OrderByDescending(x => x.Item2).Select(x => x.Item1);
        }