Exemplo n.º 1
0
        public static SchemaMapping[] Compile(DataContextModel model)
        {
            var options = new CompilerParameters(References.Select(a => a.Location).ToArray())
            {
                GenerateInMemory = true
            };

            try {
                var sourceFile = options.TempFiles.AddExtension("cs");

                using (var writer = new StreamWriter(sourceFile))
                    model.WriteClasses(writer);

                var compiler = new CSharpCodeProvider(new Dictionary <string, string> {
                    { "CompilerVersion", "v4.0" }
                });

                var results = compiler.CompileAssemblyFromFile(options, sourceFile);

                if (results.Errors.Count > 0)
                {
                    throw new InvalidOperationException(results.Errors.Cast <CompilerError>().Join(Environment.NewLine, ce => ce.ErrorText));
                }

                return
                    (model.Schemas.Select(s =>
                                          (SchemaMapping)
                                          results.CompiledAssembly
                                          .GetType(model.Namespace + "." + s.RowClassName)
                                          .GetProperty("SchemaMapping").GetValue(null, null)
                                          ).SortDependencies(sm => sm.Schema)
                     .ToArray());
            } finally { options.TempFiles.Delete(); }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Return existing or define new model for additional schema.
        /// </summary>
        /// <param name="dataContext">Data context model.</param>
        /// <param name="schemaName">Schema name.</param>
        /// <returns>Additional schema model.</returns>
        private AdditionalSchemaModel GetOrAddAdditionalSchema(DataContextModel dataContext, string schemaName)
        {
            if (!dataContext.AdditionalSchemas.TryGetValue(schemaName, out var schemaModel))
            {
                if (!_options.DataModel.SchemaMap.TryGetValue(schemaName, out var baseName))
                {
                    baseName = schemaName;
                }

                var schemaClassName = _namingServices.NormalizeIdentifier(
                    _options.DataModel.SchemaClassNameOptions,
                    baseName);

                var contextPropertyName = _namingServices.NormalizeIdentifier(
                    _options.DataModel.SchemaPropertyNameOptions,
                    baseName);

                var wrapperClass = new ClassModel(_options.CodeGeneration.ClassPerFile ? schemaClassName : dataContext.Class.FileName !, schemaClassName)
                {
                    Modifiers = Modifiers.Public | Modifiers.Partial | Modifiers.Static,
                    Namespace = _options.CodeGeneration.Namespace
                };
                var contextClass = new ClassModel("DataContext")
                {
                    Modifiers = Modifiers.Public | Modifiers.Partial
                };
                schemaModel = new AdditionalSchemaModel(contextPropertyName, wrapperClass, contextClass);
                dataContext.AdditionalSchemas.Add(schemaName, schemaModel);
            }

            return(schemaModel);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Generates aggregate function model from schema data.
        /// </summary>
        /// <param name="dataContext">Data context model.</param>
        /// <param name="func">Function schema.</param>
        /// <param name="defaultSchemas">List of default database schema names.</param>
        private void BuildAggregateFunction(DataContextModel dataContext, AggregateFunction func, ISet <string> defaultSchemas)
        {
            var(name, isNonDefaultSchema) = ProcessObjectName(func.Name, defaultSchemas);

            var method = new MethodModel(
                _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureNameOptions,
                                                    (func.Name.Package != null ? $"{func.Name.Package}_" : null) + name.Name))
            {
                Modifiers = Modifiers.Public | Modifiers.Static | Modifiers.Extension,
                Summary   = func.Description,
            };

            var metadata = new FunctionMetadata()
            {
                Name           = name,
                ServerSideOnly = true,
                IsAggregate    = true
            };

            if (func.Parameters.Count > 0)
            {
                metadata.ArgIndices = new int[func.Parameters.Count];
                for (var i = 0; i < metadata.ArgIndices.Length; i++)
                {
                    metadata.ArgIndices[i] = i + 1;
                }
            }

            // just a guard
            if (func.Result is not ScalarResult scalarResult)
            {
                throw new InvalidOperationException($"Aggregate function {func.Name} returns non-scalar value.");
            }

            var typeMapping = MapType(scalarResult.Type);
            var funcModel   = new AggregateFunctionModel(name, method, metadata, typeMapping.CLRType.WithNullability(scalarResult.Nullable));

            BuildParameters(func.Parameters, funcModel.Parameters);

            _interceptors.PreprocessAggregateFunction(_languageProvider.TypeParser, funcModel);

            if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType)
            {
                GetOrAddAdditionalSchema(dataContext, func.Name.Schema !).AggregateFunctions.Add(funcModel);
            }
            else
            {
                dataContext.AggregateFunctions.Add(funcModel);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Generates table function model from schema data.
        /// </summary>
        /// <param name="dataContext">Data context model.</param>
        /// <param name="func">Function schema.</param>
        /// <param name="defaultSchemas">List of default database schema names.</param>
        private void BuildTableFunction(DataContextModel dataContext, TableFunction func, ISet <string> defaultSchemas)
        {
            var(name, isNonDefaultSchema) = ProcessObjectName(func.Name, defaultSchemas);

            var method = new MethodModel(
                _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureNameOptions,
                                                    (func.Name.Package != null ? $"{func.Name.Package}_" : null) + name.Name))
            {
                Modifiers = Modifiers.Public,
                Summary   = func.Description
            };

            var metadata = new TableFunctionMetadata()
            {
                Name = name
            };
            var funcModel = new TableFunctionModel(
                name,
                method,
                metadata,
                _namingServices.NormalizeIdentifier(_options.DataModel.TableFunctionMethodInfoFieldNameOptions, func.Name.Name))
            {
                Error = func.SchemaError?.Message
            };

            BuildParameters(func.Parameters, funcModel.Parameters);

            if (func.Result != null)
            {
                funcModel.Result = PrepareResultSetModel(func.Name, func.Result);
            }

            _interceptors.PreprocessTableFunction(_languageProvider.TypeParser, funcModel);

            if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType)
            {
                GetOrAddAdditionalSchema(dataContext, func.Name.Schema !).TableFunctions.Add(funcModel);
            }
            else
            {
                dataContext.TableFunctions.Add(funcModel);
            }
        }
Exemplo n.º 5
0
        public SchemaDetailForm(DataContextModel context, SchemaModel schema)
        {
            InitializeComponent();
            this.schema  = schema;
            this.context = context;

            dataTypeEdit.Items.AddRange(standardColumnTypes);
            dataTypeEdit.DropDownRows = standardColumnTypes.Length;

            columnPickerEdit.DataSource    = schema.Columns;
            schemaBindingSource.DataSource = schema;
            schemaVGrid.DataSource         = new[] { schema };

            UpdateNames();

            UpdateForeignSchemaEdit(true);
            UpdateColumnSqlNameEdit();

            schema.PropertyChanged      += schema_PropertyChanged;
            context.Schemas.ListChanged += Schemas_ListChanged;
            columnsView.BestFitColumns();
            columnPickerEdit.View.BestFitColumns();
        }
        /// <summary>
        /// Creates empty data context class model for current schema and initialize it with basic properties.
        /// </summary>
        /// <returns>Data context model instance.</returns>
        private DataContextModel BuildDataContext()
        {
            string className;

            if (_options.DataModel.ContextClassName != null)
            {
                // name provided by user and shouldn't be modified except cases when it is invalid
                className = _namingServices.NormalizeIdentifier(
                    NormalizationOptions.None,
                    _options.DataModel.ContextClassName);
            }
            else
            {
                className = _namingServices.NormalizeIdentifier(
                    _options.DataModel.DataContextClassNameOptions,
                    _schemaProvider.DatabaseName != null ? (_schemaProvider.DatabaseName + "DB") : "MyDataContext");
            }

            var dataContextClass = new ClassModel(className, className)
            {
                Modifiers = Modifiers.Public | Modifiers.Partial,
                Namespace = _options.CodeGeneration.Namespace
            };

            if (_options.DataModel.BaseContextClass != null)
            {
                dataContextClass.BaseType = _languageProvider.TypeParser.Parse(_options.DataModel.BaseContextClass, false);
            }
            else
            {
                dataContextClass.BaseType = WellKnownTypes.LinqToDB.Data.DataConnection;
            }

            if (_options.DataModel.IncludeDatabaseInfo)
            {
                var summary = new StringBuilder();
                if (_schemaProvider.DatabaseName != null)
                {
                    summary.AppendFormat("Database       : {0}", _schemaProvider.DatabaseName).AppendLine();
                }
                if (_schemaProvider.DataSource != null)
                {
                    summary.AppendFormat("Data Source    : {0}", _schemaProvider.DataSource).AppendLine();
                }
                if (_schemaProvider.ServerVersion != null)
                {
                    summary.AppendFormat("Server Version : {0}", _schemaProvider.ServerVersion).AppendLine();
                }

                if (summary.Length > 0)
                {
                    dataContextClass.Summary = summary.ToString();
                }
            }

            var dataContext = new DataContextModel(dataContextClass);

            dataContext.HasDefaultConstructor        = _options.DataModel.HasDefaultConstructor;
            dataContext.HasConfigurationConstructor  = _options.DataModel.HasConfigurationConstructor;
            dataContext.HasUntypedOptionsConstructor = _options.DataModel.HasUntypedOptionsConstructor;
            dataContext.HasTypedOptionsConstructor   = _options.DataModel.HasTypedOptionsConstructor;

            return(dataContext);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Generates scalar function model from schema data.
        /// </summary>
        /// <param name="dataContext">Data context model.</param>
        /// <param name="func">Function schema.</param>
        /// <param name="defaultSchemas">List of default database schema names.</param>
        private void BuildScalarFunction(DataContextModel dataContext, ScalarFunction func, ISet <string> defaultSchemas)
        {
            var(name, isNonDefaultSchema) = ProcessObjectName(func.Name, defaultSchemas);

            var method = new MethodModel(
                _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureNameOptions,
                                                    (func.Name.Package != null ? $"{func.Name.Package}_" : null) + name.Name))
            {
                Modifiers = Modifiers.Public | Modifiers.Static,
                Summary   = func.Description
            };

            var metadata = new FunctionMetadata()
            {
                Name           = name,
                ServerSideOnly = true
            };

            var funcModel = new ScalarFunctionModel(name, method, metadata);

            BuildParameters(func.Parameters, funcModel.Parameters);

            // thanks to pgsql, scalar function could return not only scalar, but also tuple or just nothing
            switch (func.Result.Kind)
            {
            case ResultKind.Scalar:
            {
                var scalarResult = (ScalarResult)func.Result;
                var typeMapping  = MapType(scalarResult.Type);
                funcModel.Return = typeMapping.CLRType.WithNullability(scalarResult.Nullable);
                // TODO: DataType not used by current scalar function mapping API
                break;
            }

            case ResultKind.Tuple:
            {
                var tupleResult = (TupleResult)func.Result;

                // tuple model class
                var @class = new ClassModel(
                    _namingServices.NormalizeIdentifier(
                        _options.DataModel.FunctionTupleResultClassNameOptions,
                        func.Name.Name))
                {
                    Modifiers = Modifiers.Public | Modifiers.Partial
                };
                funcModel.ReturnTuple = new TupleModel(@class)
                {
                    CanBeNull = tupleResult.Nullable
                };

                // fields order must be preserved, as tuple fields mapped by ordinal
                foreach (var field in tupleResult.Fields)
                {
                    var typeMapping = MapType(field.Type);

                    var prop = new PropertyModel(_namingServices.NormalizeIdentifier(_options.DataModel.FunctionTupleResultPropertyNameOptions, field.Name ?? "Field"), typeMapping.CLRType.WithNullability(field.Nullable))
                    {
                        Modifiers = Modifiers.Public,
                        IsDefault = true,
                        HasSetter = true
                    };
                    funcModel.ReturnTuple.Fields.Add(new TupleFieldModel(prop, field.Type)
                        {
                            DataType = typeMapping.DataType
                        });
                }
                break;
            }

            case ResultKind.Void:
                // just regular postgresql void function, nothing to see here...
                // because function must have return type to be callable in query, we set return type to object?
                funcModel.Return = WellKnownTypes.System.ObjectNullable;
                break;
            }

            _interceptors.PreprocessScalarFunction(_languageProvider.TypeParser, funcModel);

            if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType)
            {
                GetOrAddAdditionalSchema(dataContext, func.Name.Schema !).ScalarFunctions.Add(funcModel);
            }
            else
            {
                dataContext.ScalarFunctions.Add(funcModel);
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Generates stored procedure model from schema data.
        /// </summary>
        /// <param name="dataContext">Data context model.</param>
        /// <param name="func">Function schema.</param>
        /// <param name="defaultSchemas">List of default database schema names.</param>
        private void BuildStoredProcedure(DataContextModel dataContext, StoredProcedure func, ISet <string> defaultSchemas)
        {
            var(name, isNonDefaultSchema) = ProcessObjectName(func.Name, defaultSchemas);

            var method = new MethodModel(
                _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureNameOptions,
                                                    (func.Name.Package != null ? $"{func.Name.Package}_" : null) + name.Name))
            {
                Modifiers = Modifiers.Public | Modifiers.Static | Modifiers.Extension,
                Summary   = func.Description,
            };

            var funcModel = new StoredProcedureModel(name, method)
            {
                Error = func.SchemaError?.Message
            };

            BuildParameters(func.Parameters, funcModel.Parameters);

            switch (func.Result.Kind)
            {
            case ResultKind.Void:
                break;

            case ResultKind.Tuple:
                // no support from db (maybe pgsql could do it?) and schema API now
                throw new NotImplementedException($"Tuple return type support not implemented for stored procedures");

            case ResultKind.Scalar:
            {
                var scalarResult = (ScalarResult)func.Result;
                var typeMapping  = MapType(scalarResult.Type);

                var paramName = _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureParameterNameOptions, scalarResult.Name ?? "return");
                funcModel.Return = new FunctionParameterModel(
                    new ParameterModel(paramName, typeMapping.CLRType.WithNullability(scalarResult.Nullable),
                                       CodeParameterDirection.Out),
                    System.Data.ParameterDirection.ReturnValue)
                {
                    Type       = scalarResult.Type,
                    DataType   = typeMapping.DataType,
                    DbName     = scalarResult.Name,
                    IsNullable = scalarResult.Nullable
                };
                break;
            }
            }

            FunctionResult?resultModel = null;

            if (func.ResultSets?.Count > 1)
            {
                // TODO: to support multi-result sets we need at least one implementation in schema provider
                throw new NotImplementedException($"Multi-set stored procedures not supported");
            }
            else if (func.ResultSets?.Count == 1)
            {
                funcModel.Results.Add(resultModel = PrepareResultSetModel(func.Name, func.ResultSets[0]));
            }

            // prepare async result class descriptor if needed
            var returningParameters = funcModel.Parameters.Where(p => p.Direction != System.Data.ParameterDirection.Input).ToList();

            if (funcModel.Return != null)
            {
                returningParameters.Add(funcModel.Return);
            }
            if (returningParameters.Count > 0)
            {
                var asyncResult = new AsyncProcedureResult(
                    new ClassModel(
                        _namingServices.NormalizeIdentifier(
                            _options.DataModel.AsyncProcedureResultClassNameOptions,
                            func.Name.Name))
                {
                    Modifiers = Modifiers.Public
                }, new PropertyModel("Result")
                {
                    Modifiers = Modifiers.Public,
                    IsDefault = true,
                    HasSetter = true
                });

                foreach (var parameter in returningParameters)
                {
                    asyncResult.ParameterProperties.Add(
                        parameter,
                        new PropertyModel(_namingServices.NormalizeIdentifier(_options.DataModel.AsyncProcedureResultClassPropertiesNameOptions, parameter.Parameter.Name), parameter.Parameter.Type)
                    {
                        Modifiers = Modifiers.Public,
                        IsDefault = true,
                        HasSetter = true
                    });
                }

                // TODO: next line will need refactoring if we add multi-set support
                funcModel.Results.Clear();
                funcModel.Results.Add(new FunctionResult(resultModel?.CustomTable, resultModel?.Entity, asyncResult));
            }

            _interceptors.PreprocessStoredProcedure(_languageProvider.TypeParser, funcModel);

            if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType)
            {
                GetOrAddAdditionalSchema(dataContext, func.Name.Schema !).StoredProcedures.Add(funcModel);
            }
            else
            {
                dataContext.StoredProcedures.Add(funcModel);
            }
        }
Exemplo n.º 9
0
        public static IEnumerable <SchemaModel> ReadSchemas(DataContextModel owner, DBConnector database)
        {
            if (database == null)
            {
                throw new ArgumentNullException("database");
            }

            List <SchemaModel> tables            = new List <SchemaModel>();
            List <Action>      postColumnActions = new List <Action>();

            Func <string, string, SchemaModel> Table = (schema, name) =>                //First look for a new table from SQL Server, then for an existing one.
                                                       String.IsNullOrEmpty(name) ? null : tables.SingleOrDefault(t => t.SqlSchemaName == schema && t.SqlName == name)
                                                       ?? owner.Schemas.SingleOrDefault(t => t.SqlSchemaName == schema && t.SqlName == name);

            using (var connection = database.OpenConnection()) {
                #region Read Tables
                using (var reader = database.ExecuteReader(TablesSql)) {
                    while (reader.Read())
                    {
                        var table = new SchemaModel(owner)
                        {
                            Name          = (string)reader["TableName"],
                            SqlName       = (string)reader["TableName"],
                            SqlSchemaName = reader["SchemaName"] as string
                        };

                        if (Table(table.SqlSchemaName, table.SqlName) != null)
                        {
                            continue;                                                                                   //TODO: Import column
                        }
                        string keyName = reader["PrimaryKeyName"] as string;
                        if (!String.IsNullOrEmpty(keyName))
                        {
                            postColumnActions.Add(
                                () => table.PrimaryKey = table.Columns.Single(c => c.SqlName == keyName)
                                );
                        }

                        tables.Add(table);
                    }
                }
                #endregion

                using (var reader = database.ExecuteReader(ColumnsSql)) {
                    while (reader.Read())
                    {
                        var table = Table((string)reader["SchemaName"], (string)reader["TableName"]);
                        if (table == null)
                        {
                            continue;                                           //Skip tables without RowVersion columns
                        }
                        var name = (string)reader["ColumnName"];
                        if (table.Columns.Any(c => c.SqlName == name))
                        {
                            continue;                                   //Don't add duplicate columns to existing tables.
                        }
                        table.Columns.Add(new ColumnModel(table)
                        {
                            Name     = name,
                            SqlName  = name,
                            DataType = SqlTypes[(string)reader["DataType"]],

                            AllowNulls = 1 == (int)reader["AllowNulls"],
                            IsUnique   = 1 == (int)reader["IsUnique"],

                            ForeignSchema = Table(reader["ForeignSchema"] as string, reader["ForeignTable"] as string)
                        });
                    }
                }
            }

            postColumnActions.ForEach(a => a());

            return(tables);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Creates entity model from table/view schema information.
        /// </summary>
        /// <param name="dataContext">Current data model's data context descriptor.</param>
        /// <param name="table">Table or view schema data.</param>
        /// <param name="defaultSchemas">List of default database schema names.</param>
        /// <param name="baseType">Optional base entity class type.</param>
        private void BuildEntity(
            DataContextModel dataContext,
            TableLikeObject table,
            ISet <string> defaultSchemas,
            IType?baseType)
        {
            var(tableName, isNonDefaultSchema) = ProcessObjectName(table.Name, defaultSchemas);

            var metadata = new EntityMetadata()
            {
                Name   = tableName,
                IsView = table is View
            };

            // generate name for entity table property in data context class
            var contextPropertyName = _options.DataModel.EntityContextPropertyNameProvider?.Invoke(table);

            contextPropertyName = contextPropertyName != null
                                ? contextPropertyName
                                : _namingServices.NormalizeIdentifier(
                _options.DataModel.EntityContextPropertyNameOptions,
                table.Name.Name);

            // generate entity class name
            var className          = _options.DataModel.EntityClassNameProvider?.Invoke(table);
            var hasCustomClassName = className != null;

            className = className != null
                                ? className
                                : _namingServices.NormalizeIdentifier(
                _options.DataModel.EntityClassNameOptions,
                table.Name.Name);

            // add schema name ato entity class name as prefix for table from non-default schema without
            // class-per-schema option set
            if (!hasCustomClassName && !_options.DataModel.GenerateSchemaAsType && isNonDefaultSchema)
            {
                className = table.Name.Schema + "_" + className;
            }

            // entity class properties
            var classModel = new ClassModel(_options.CodeGeneration.ClassPerFile ? className : dataContext.Class.FileName !, className);

            classModel.Summary   = table.Description;
            classModel.BaseType  = baseType;
            classModel.Namespace = _options.CodeGeneration.Namespace;
            classModel.Modifiers = Modifiers.Public;
            if (_options.DataModel.EntityClassIsPartial)
            {
                classModel.Modifiers = classModel.Modifiers | Modifiers.Partial;
            }

            // entity data model
            var entity = new EntityModel(
                metadata,
                classModel,
                contextPropertyName == null
                                        ? null
                // note that property type is open-generic here
                // concrete type argument will be set later during AST generation
                                        : new PropertyModel(contextPropertyName, WellKnownTypes.LinqToDB.ITableT)
            {
                Modifiers = Modifiers.Public,
                Summary   = table.Description
            });

            entity.ImplementsIEquatable = _options.DataModel.GenerateIEquatable;
            entity.FindExtensions       = _options.DataModel.GenerateFindExtensions;

            // add entity to lookup
            _entities.Add(table.Name, new TableWithEntity(table, entity));

            BuildEntityColumns(table, entity);

            // call interceptor after entity model completely configured
            _interceptors.PreprocessEntity(_languageProvider.TypeParser, entity);

            // add entity to model
            if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType)
            {
                GetOrAddAdditionalSchema(dataContext, table.Name.Schema !).Entities.Add(entity);
            }
            else
            {
                dataContext.Entities.Add(entity);
            }
        }