Пример #1
0
        protected override Expression VisitConstant(ConstantExpression node)
        {
#if (DEBUGPRINT)
            System.Diagnostics.Debug.Print("VisitConstant: {0}", node);
#endif

            var expression = base.VisitConstant(node);

            var type = node.Type;
            if (IsNullableType(type))
            {
                var genericArgs = type.GetGenericArguments();
                if ((genericArgs != null) && (genericArgs.Length == 1))
                {
                    type = genericArgs[0];
                }
            }

            if (type == typeof(byte[]))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromBinary((byte[])node.Value));
            }
            else if (type == typeof(bool))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromBoolean((bool?)node.Value));
            }
            else if (type == typeof(byte))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromByte((byte?)node.Value));
            }
            else if (type == typeof(DateTime))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromDateTime((DateTime?)node.Value));
            }
            else if (type == typeof(DateTimeOffset))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromDateTimeOffset((DateTimeOffset?)node.Value));
            }
            else if (type == typeof(decimal))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromDecimal((decimal?)node.Value));
            }
            else if (type == typeof(double))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromDouble((double?)node.Value));
            }
            else if (type == typeof(Guid))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromGuid((Guid?)node.Value));
            }
            else if (type == typeof(Int16))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromInt16((Int16?)node.Value));
            }
            else if (type == typeof(Int32))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromInt32((Int32?)node.Value));
            }
            else if (type.IsEnum)
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromInt32((Int32)node.Value));
            }
            else if (type == typeof(Int64))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromInt64((Int64?)node.Value));
            }
            else if (type == typeof(float))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromSingle((float?)node.Value));
            }
            else if (type == typeof(string))
            {
                MapExpressionToDbExpression(expression, DbConstantExpression.FromString((string)node.Value));
            }
            else
            {
                throw new NotImplementedException(string.Format("Unhandled Type of {0} for Constant value {1} in LambdaToDbExpressionVisitor.VisitConstant", node.Type.Name, node.Value ?? "null"));
            }

            return(expression);
        }
Пример #2
0
        //  This is called for any navigation property reference so we can apply filters for those entities here.
        //  That includes any navigation properties referenced in functions (.Where() clauses) and also any
        //  child entities that are .Include()'d.
        public override DbExpression Visit(DbPropertyExpression expression)
        {
#if DEBUG_VISITS
            System.Diagnostics.Debug.Print("Visit(DbPropertyExpression): EdmType.Name={0}", expression.ResultType.ModelTypeUsage.EdmType.Name);
#endif
            var baseResult = base.Visit(expression);

            var basePropertyResult = baseResult as DbPropertyExpression;
            if (basePropertyResult == null)
            {
                return(baseResult);      //  base.Visit changed type!
            }
            var navProp = basePropertyResult.Property as NavigationProperty;
            if (navProp != null)
            {
                var targetEntityType = navProp.ToEndMember.GetEntityType();

                string entityName = targetEntityType.Name;
                var    containers = _ObjectContext.MetadataWorkspace.GetItems <EntityContainer>(DataSpace.CSpace).First();
                var    filterList = FindFiltersForEntitySet(targetEntityType.MetadataProperties, containers);

                if (filterList.Any())
                {
                    //  If the expression contains a collection (i.e. the child property is an IEnumerable), we can bind directly to it.
                    //  Otherwise, we have to create a DbScanExpression over the ResultType in order to bind.
                    if (baseResult.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)
                    {
                        var binding             = DbExpressionBuilder.Bind(baseResult);
                        var newFilterExpression = BuildFilterExpressionWithDynamicFilters(entityName, filterList, binding, null);
                        if (newFilterExpression != null)
                        {
                            //  If not null, a new DbFilterExpression has been created with our dynamic filters.
                            return(newFilterExpression);
                        }
                    }
                    else if (baseResult.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType)
                    {
                        if (DoesNotSupportElementMethod(_DbContext))
                        {
                            //  Oracle and MySQL do not support the "newFilterExpression.Element()" method that we need to call
                            //  at the end of this block.  Oracle *MAY* support it in a newer release but not sure
                            //  (see https://community.oracle.com/message/10168766#10168766).
                            //  But users may not have the option of upgrading their database so decided to try to support it.
                            //  If we find it is supported by newer versions, can detect those versions and allow the normal handling.
                            //  To apply any necessary filters to these entities, we're going to have to do it using SSpace.
                            //  These entities will be visited via the DbScan visit method so we will apply filters there.
                            //  If one of those filters then references a child property, the filter will fail.
                            return(baseResult);
                        }

                        var entitySet = containers.EntitySets.FirstOrDefault(e => e.ElementType.Name == baseResult.ResultType.EdmType.Name);
                        if (entitySet == null)
                        {
#if (DEBUG)
                            throw new ApplicationException(string.Format("EntitySet not found for {0} - this is a known issue when using TPT", baseResult.ResultType.EdmType.Name));
#else
                            return(baseResult);
#endif
                        }

                        var scanExpr = DbExpressionBuilder.Scan(entitySet);
                        var binding  = DbExpressionBuilder.Bind(scanExpr);

                        //  Build the join conditions that are needed to join from the source object (basePropertyResult.Instance)
                        //  to the child object (the scan expression we just creating the binding for).
                        //  These conditions will be and'd with the filter conditions.
                        var associationType = navProp.RelationshipType as AssociationType;
                        if (associationType == null)
                        {
                            throw new ApplicationException(string.Format("Unable to find AssociationType on navigation property of single child property {0} in type {1}", navProp.Name, navProp.DeclaringType.FullName));
                        }
                        if (associationType.Constraint == null)
                        {
                            //  KNOWN_ISSUE:
                            //  If this happens, the model does not contain the foreign key (the "id" property).  EF will automatically generate
                            //  it based on naming rules when generating the SSpace/database models but does not expose the Constraint here in the
                            //  AssociationType.  In order for us to be able to generate the conditions correctly, those Foreign Keys need to be
                            //  specified on the model.  To fix/handle this, we would need to examine the SSpace Association Sets (which do have
                            //  these relations!!) to try to map that information back to CSpace to figure out the correct properties of the FK conditions.
                            //  or...the models just need to contain the necessary "ID" properties for the FK relations so that they are available here
                            //  (in CSpace) for us to generate the necessary join conditions.
                            throw new ApplicationException(string.Format("FK Constriant not found for association '{0}' - must directly specify foreign keys on model to be able to apply this filter", associationType.FullName));
                        }

                        //  Figure out if the "baseResults" are the from side or to side of the constraint so we can create the properties correctly
                        bool baseResultIsFromRole = (basePropertyResult.Instance.ResultType.EdmType == ((AssociationEndMember)associationType.Constraint.FromRole).GetEntityType());

                        DbExpression joinCondition = null;
                        for (int i = 0; i < associationType.Constraint.FromProperties.Count; i++)
                        {
                            var prop1 = DbExpressionBuilder.Property(basePropertyResult.Instance, baseResultIsFromRole ? associationType.Constraint.FromProperties[i] : associationType.Constraint.ToProperties[i]);
                            var prop2 = DbExpressionBuilder.Property(binding.Variable, baseResultIsFromRole ? associationType.Constraint.ToProperties[i] : associationType.Constraint.FromProperties[i]);

                            var condition = prop1.Equal(prop2) as DbExpression;
                            joinCondition = (joinCondition == null) ? condition : joinCondition.And(condition);
                        }

                        //  Translate the filter predicate into a DbExpression bound to the Scan expression of the target entity set.
                        //  Those conditions are then and'd with the join conditions necessary to join the target table with the source table.
                        var newFilterExpression = BuildFilterExpressionWithDynamicFilters(entityName, filterList, binding, joinCondition);
                        if (newFilterExpression != null)
                        {
                            //  Converts the collection results into a single row.  The expected output is a single item so EF will
                            //  then populate the results of that query into the property in the model.
                            //  The resulting SQL will be a normal "left outer join" just as it would normally be except that our
                            //  filter predicate conditions will be included with the normal join conditions.

                            //  MySQL needs this Limit() applied here or it throws an error saying:
                            //  Unable to cast object of type 'MySql.Data.Entity.SelectStatement' to type 'MySql.Data.Entity.LiteralFragment'.
                            //  But don't do that unless necessary because it produces extra "outer apply" sub queries in MS SQL.
                            //  This trick does not work for Oracle...
                            if (_DbContext.IsMySql())
                            {
                                return(newFilterExpression.Limit(DbConstantExpression.FromInt32(1)).Element());
                            }

                            return(newFilterExpression.Element());
                        }
                    }
                }
            }

            return(baseResult);
        }