/// <summary>
        /// Performs the required includes on the specified <paramref name="queryable"/>
        /// </summary>
        /// <param name="queryable">The queryable</param>
        /// <param name="joinMap">The join map</param>
        /// <returns></returns>
        private IQueryable PerformThenInclude(IQueryable queryable, JoinMap joinMap)
        {
            // Get the ThenInclude<TEntity, TPreviousProperty, TProperty> method
            var thenIncludeMethod = typeof(EntityFrameworkQueryableExtensions).GetMethods().Where(x => x.Name == nameof(EntityFrameworkQueryableExtensions.ThenInclude)).ElementAt(0);

            // Get the type of the model
            var modelType = queryable.GetType().GetGenericArguments()[0];

            // Get the previous property type
            var previousPropertyType = TypeHelpers.GetNonEnumerableType(queryable.GetType().GetGenericArguments()[1]);

            // Get the navigation property name
            var navigationPropertyName = CeidDiplomatikiHelpers.GetPluralForm(joinMap.ReferencedTable.TableName);

            // Get the navigation property
            var navigationProperty = previousPropertyType.GetProperty(navigationPropertyName);

            // Create the expression
            var expression = ExpressionHelpers.CreatePropertySelectorExpression(previousPropertyType, navigationProperty);

            // Set the generic arguments (TEntity, TPreviousProperty, TProperty)
            thenIncludeMethod = thenIncludeMethod.MakeGenericMethod(modelType, previousPropertyType, navigationProperty.PropertyType);

            // Call the ThenInclude method
            queryable = (IQueryable)thenIncludeMethod.Invoke(null, new object[] { queryable, expression });

            // Return the queryable
            return(queryable);
        }
        /// <summary>
        /// Performs the required includes on the specified <paramref name="queryable"/>
        /// </summary>
        /// <param name="queryable">The queryable</param>
        /// <param name="initialTable">The initial table</param>
        /// <returns></returns>
        private IQueryable PerformIncludes(IQueryable queryable, IDbProviderTable initialTable)
        {
            // For every join...
            foreach (var join in Joins)
            {
                var joinsPath = ComputePath(join);

                // For every related join...
                foreach (var j in joinsPath)
                {
                    // Get the type of the model
                    var modelType = queryable.GetType().GetGenericArguments()[0];

                    // If it's the first join...
                    if (joinsPath.ElementAt(0) == j)
                    {
                        // Get the navigation property name
                        var navigationPropertyName = CeidDiplomatikiHelpers.GetPluralForm(j.ReferencedTable.TableName);

                        // Get the navigation property
                        var navigationProperty = modelType.GetProperty(navigationPropertyName);

                        // Create the expression
                        var expression = ExpressionHelpers.CreatePropertySelectorExpression(modelType, navigationProperty);

                        // Get the Include<TEntity, TProperty> method
                        var includeMethod = typeof(EntityFrameworkQueryableExtensions).GetMethods().First(x => x.Name == nameof(EntityFrameworkQueryableExtensions.Include) && x.GetParameters()[1].ParameterType != typeof(string));

                        // Set the generic arguments (TEntity and TProperty)
                        includeMethod = includeMethod.MakeGenericMethod(modelType, navigationProperty.PropertyType);

                        // Call the Include method
                        queryable = (IQueryable)includeMethod.Invoke(null, new object[] { queryable, expression });

                        // Continue
                        continue;
                    }

                    queryable = PerformThenInclude(queryable, j);
                }
            }

            // Return the queryable
            return(queryable);
        }
Beispiel #3
0
        /// <summary>
        /// Creates and returns a <see cref="QueryMap"/> from the specified <paramref name="dataModel"/>.
        /// NOTE: If the model is not set, then the default values are used!
        /// </summary>
        /// <param name="databaseOptions">The database options</param>
        /// <param name="database">The database that contains the mapped table</param>
        /// <param name="columns">The columns of the tables</param>
        /// <param name="tables">The tables</param>
        /// <param name="joins">The joins</param>
        /// <param name="dataModel">The data model</param>
        /// <returns></returns>
        public static QueryMap FromDataModel(BaseDatabaseOptionsDataModel databaseOptions, IDbProviderDatabase database, IEnumerable <IDbProviderTable> tables, IEnumerable <IDbProviderColumn> columns, IEnumerable <JoinMap> joins, QueryMapDataModel dataModel = null)
        {
            // Create the map
            var queryMap = new QueryMap(databaseOptions, database, tables, columns, joins);

            // If there is a data model...
            if (dataModel != null)
            {
                queryMap.Id          = dataModel.Id;
                queryMap.Color       = dataModel.Color;
                queryMap.Description = dataModel.Description;
                queryMap.Name        = dataModel.Name;
            }

            var tableToTypeBuilderMapper = new Dictionary <IDbProviderTable, TypeBuilder>();

            // For every table...
            foreach (var table in tables)
            {
                // Create the builder
                var typeBuilder = CeidDiplomatikiHelpers.GetTypeBuilder(queryMap.Id + "-" + table.TableName);

                // For every table column...
                foreach (var column in columns.Where(x => x.TableName == table.TableName))
                {
                    // Create a property
                    TypeBuilderHelpers.CreateProperty(typeBuilder, column.ColumnName, column.DataType);
                }

                // Map it
                tableToTypeBuilderMapper.Add(table, typeBuilder);
            }

            // For every join...
            foreach (var join in joins)
            {
                // Get the principle model type builder
                var principleModelTypeBuilder = tableToTypeBuilderMapper.First(x => x.Key == join.Table).Value;

                // Get the referenced model type builder
                var referencedModelTypeBuilder = tableToTypeBuilderMapper.First(x => x.Key == join.ReferencedTable).Value;

                // Create the principle navigation property type
                var principleNavigationPropertyType = typeof(IEnumerable <>).MakeGenericType(referencedModelTypeBuilder);

                // Add it to the principle model
                TypeBuilderHelpers.CreateProperty(principleModelTypeBuilder, CeidDiplomatikiHelpers.GetPluralForm(join.ReferencedTable.TableName), principleNavigationPropertyType);

                // Add the foreign navigation property type
                TypeBuilderHelpers.CreateProperty(referencedModelTypeBuilder, join.Table.TableName, principleModelTypeBuilder);
            }

            var tableToTypeMapper = new Dictionary <IDbProviderTable, Type>();

            // For every table to type builder map...
            foreach (var map in tableToTypeBuilderMapper)
            {
                // Build the type
                var type = map.Value.CreateType();

                if (joins.Any(x => x.Index == 0 && x.Table.TableName == map.Key.TableName))
                {
                    queryMap.RootType = type;
                }
                else if (joins.Count() == 0)
                {
                    queryMap.RootType = type;
                }

                // Map it
                tableToTypeMapper.Add(map.Key, type);
            }

            // Set the types to the query
            queryMap.DataModelTypes = tableToTypeMapper.Select(x => x.Value).ToList();

            // Create the db context type builder
            var dbContextTypeBuilder = CeidDiplomatikiHelpers.GetTypeBuilder(Guid.NewGuid().ToString() + "-DbContext");

            // Set the base type
            var baseType = typeof(PresenterDbContext);

            // Inherit from the PresenterDbContext
            dbContextTypeBuilder.SetParent(baseType);

            // For every data model type...
            foreach (var map in tableToTypeMapper)
            {
                // Create the DbSet type that will be set as a property to the DbContext
                var dbSetType = typeof(DbSet <>).MakeGenericType(map.Value);

                // Add it to the type
                TypeBuilderHelpers.CreateProperty(dbContextTypeBuilder, map.Key.TableName, dbSetType);
            }

            // Create the constructors
            TypeBuilderHelpers.CreatePassThroughConstructors(dbContextTypeBuilder, baseType);

            // Create the db context type
            queryMap.DbContextType = dbContextTypeBuilder.CreateType();

            // If there is a data model...
            // NOTE: We create the column and the data presenter maps at the end of the initialization
            //       because some properties of the query map are required to get set!
            if (dataModel != null)
            {
                dataModel.PropertyMaps.ForEach(model => queryMap.Add(PropertyMap.FromDataModel(queryMap, queryMap.DataModelTypes.First(x => queryMap.GetTableName(x) == model.TableName), dataModel.PropertyMaps.First(x => x.TableName == model.TableName && x.ColumnName == model.ColumnName))));
                dataModel.DataGridPresenterMaps.ForEach(model => queryMap.Add(DataGridPresenterMap.FromDataModel(queryMap, model)));
            }

            // Return the map
            return(queryMap);
        }
Beispiel #4
0
        /// <summary>
        /// Override this method to further configure the model that was discovered by convention
        /// from the entity types exposed in Microsoft.EntityFrameworkCore.DbSet`1 properties
        /// on your derived context. The resulting model may be cached and re-used for subsequent
        /// instances of your derived context.
        /// </summary>
        /// <param name="modelBuilder">The model builder</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // For every DbSet...
            foreach (var dbSetProperty in GetType().GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(DbSet <>)))
            {
                // Get the table
                var table = QueryMap.Tables.First(x => x.TableName == dbSetProperty.Name);

                // Get the name of the primary key column
                var primaryKeyColumnName = QueryMap.Columns.First(x => x.TableName == table.TableName && x.IsPrimaryKey).ColumnName;

                // Get the generic type of the DbSet
                var modelType = dbSetProperty.PropertyType.GetGenericArguments()[0];

                // We need to recreate the following Fluent API command using reflection
                // modelBuilder.Entity<TModel>().HasKey(new string[]{ IdColumnName });

                // Get the Entity<> method
                var entityMethod = modelBuilder.GetType().GetMethods().First(x => x.Name == nameof(ModelBuilder.Entity) && x.IsGenericMethod)
                                   .MakeGenericMethod(modelType);

                // Call the Entity<> method
                var typeBuilder = entityMethod.Invoke(modelBuilder, null);

                // Get the HasKey method
                var hasKeyMethod = typeBuilder.GetType().GetMethods()
                                   .First(x => x.Name == nameof(EntityTypeBuilder <object> .HasKey) && x.GetParameters().Any(y => y.ParameterType == typeof(string[])));

                // Call the HasKey method
                var keyBuilder = hasKeyMethod.Invoke(typeBuilder, new object[] { new string[] { primaryKeyColumnName } });

                // For every join where the current DbSet property represents the principle table...
                foreach (var join in QueryMap.Joins.Where(x => x.Table.TableName == table.TableName))
                {
                    // We need to recreate the following Fluent API command using reflection
                    // modelBuilder.Entity<TModel>
                    //             .HasMany<TRelatedModel>(navigationName)
                    //             .WithOne<TRelatedModel>(navigationName)
                    //             .HasForeignKey<TModel, TRelatedModel>(new string[]{ foreignKeyColumnName })
                    //             .HasPrincipleKey<TModel, TRelatedModel>(new string[]{ principleKeyColumnName })
                    //             .OnDelete<TModel, TRelatedModel>(DeleteBehavior.Cascade);

                    // Get the related DbSet property
                    var relatedDbSetProperty = GetType().GetProperty(join.ReferencedTable.TableName);

                    // Get the related model type
                    var relatedModelType = relatedDbSetProperty.PropertyType.GetGenericArguments()[0];

                    // Get the HasMany method
                    var hasManyMethod = typeBuilder.GetType().GetMethods().First(x => x.Name == nameof(EntityTypeBuilder <object> .HasMany) && x.IsGenericMethod && x.GetParameters()[0].ParameterType == typeof(string));

                    // Call the HasMany method
                    var collectionNavigationBuilder = hasManyMethod.MakeGenericMethod(relatedModelType).Invoke(typeBuilder, new object[] { CeidDiplomatikiHelpers.GetPluralForm(join.ReferencedTable.TableName) });

                    // Get the WithOne method
                    var withOneMethod = collectionNavigationBuilder.GetType().GetMethods().First(x => x.Name == nameof(CollectionNavigationBuilder <object, object> .WithOne) && x.GetParameters()[0].ParameterType == typeof(string));

                    // Call the WithOne method
                    var referenceCollectionBuilder = withOneMethod.Invoke(collectionNavigationBuilder, new object[] { join.Table.TableName });

                    // Get The HasForeignKey method
                    var hasForeignKeyMethod = referenceCollectionBuilder.GetType().GetMethods().First(x => x.Name == nameof(ReferenceCollectionBuilder <object, object> .HasForeignKey) && x.GetParameters()[0].ParameterType == typeof(string[]));

                    // Call the HasForeignKey method
                    hasForeignKeyMethod.Invoke(referenceCollectionBuilder, new object[] { new string[] { join.ForeignKeyColumn.ColumnName } });

                    // Get the HasPrincipleKey method
                    var hasPrincipleKeyMethod = referenceCollectionBuilder.GetType().GetMethods().First(x => x.Name == nameof(ReferenceCollectionBuilder <object, object> .HasPrincipalKey) && x.GetParameters()[0].ParameterType == typeof(string[]));

                    // Call the HasPrincipleKey method
                    hasPrincipleKeyMethod.Invoke(referenceCollectionBuilder, new object[] { new string[] { join.PrincipleKeyColumn.ColumnName } });

                    // Get the OnDelete method
                    var onDeleteMethod = referenceCollectionBuilder.GetType().GetMethods().First(x => x.Name == nameof(ReferenceCollectionBuilder <object, object> .OnDelete));

                    // Call the OnDelete method
                    // TODO: We should get the constraint condition from the analyzer and then user it to configure the delete behavior.
                    onDeleteMethod.Invoke(referenceCollectionBuilder, new object[] { DeleteBehavior.Cascade });
                }
            }
        }
        /// <summary>
        /// Gets the data using the specified <paramref name="args"/>.
        /// NOTE: Only root types are returned by this method!
        /// </summary>
        /// <param name="args">The args</param>
        /// <returns></returns>
        public override async Task <IFailable <IEnumerable> > GetDataAsync(DataGridPresenterArgs args)
        {
            // Create the result
            var result = new Failable <IEnumerable>();

            try
            {
                var queryable = GetDataQuery;

                // Get the data model type
                var dataModelType = PresenterMap.QueryMap.RootType;

                // If there is a date column...
                if (DataGridMap.DateColumn != null)
                {
                    // If there is an after date...
                    if (args.After != null)
                    {
                        // Add a where condition
                        queryable = CeidDiplomatikiHelpers.AddWhereCondition(queryable, dataModelType, ExpressionHelpers.CreatePropertyEqualityExpression(dataModelType, DataGridMap.DateColumn, args.After.Value.DateTime, NumericEqualityOperator.GreaterOrEqualThan));
                    }
                    // If there is a before date...
                    if (args.Before != null)
                    {
                        // Add a where condition
                        queryable = CeidDiplomatikiHelpers.AddWhereCondition(queryable, dataModelType, ExpressionHelpers.CreatePropertyEqualityExpression(dataModelType, DataGridMap.DateColumn, args.Before.Value.DateTime, NumericEqualityOperator.LessOrEqualThan));
                    }
                }

                // If there is a search value...
                if (!args.Search.IsNullOrEmpty())
                {
                    // If there are search column...
                    if (!DataGridMap.SearchColumns.IsNullOrEmpty())
                    {
                        var parameterExp          = Expression.Parameter(dataModelType, "x");
                        var methodCallExpressions = new List <MethodCallExpression>();

                        // For every search column...
                        foreach (var searchColumn in DataGridMap.SearchColumns)
                        {
                            var expr = CreateContainsExpression(parameterExp, dataModelType, searchColumn, args.Search);

                            methodCallExpressions.Add(expr);
                        }

                        Expression orExpression = null;
                        foreach (var expr in methodCallExpressions)
                        {
                            if (orExpression == null)
                            {
                                orExpression = expr;

                                continue;
                            }

                            orExpression = Expression.OrElse(orExpression, expr);
                        }

                        var lambda = Expression.Lambda(typeof(Func <,>).MakeGenericType(dataModelType, typeof(bool)), orExpression, parameterExp);

                        queryable = CeidDiplomatikiHelpers.AddWhereCondition(queryable, dataModelType, lambda);
                    }
                }

                // Add the order by condition
                queryable = CeidDiplomatikiHelpers.AddOrderByDescendinCondition(queryable, dataModelType, PrimaryKeyProperty);

                // Add the skip condition
                queryable = CeidDiplomatikiHelpers.AddSkipCondition(queryable, dataModelType, args.Page * args.PerPage);

                // Add the take condition
                queryable = CeidDiplomatikiHelpers.AddTakeCondition(queryable, dataModelType, args.PerPage);

                // Get the models
                var models = await CeidDiplomatikiHelpers.ExecuteToListAsync(queryable, dataModelType);

                // Set the data
                result.Result = models;
            }
            catch (Exception ex)
            {
                // If there was an error...
                result.ErrorMessage = ex.Message;
            }

            // Return the result
            return(result);
        }