Example #1
0
        private void LoadAssociationsForEach <T>(List <T> entities, LoadOptions loadOptions)
        {
            var typeTLoadOptions = loadOptions.LoadWithOptions
                                   .Where(lo => lo.Member.Parameters.Single().Type == typeof(T))
                                   .Select(lo => new { LoadOption = lo, Member = ((MemberExpression)lo.Member.Body).Member });

            foreach (var loadOption in typeTLoadOptions)
            {
                var thisSideAssociationProperty = loadOption.Member as PropertyInfo;
                if (thisSideAssociationProperty == null)
                {
                    throw new NotSupportedException("Only properties are supported");
                }

                MetaType        type        = this.GetMetaType <T>();
                MetaAssociation association = type.Associations.Where(assoc => assoc.ThisMember.Name == thisSideAssociationProperty.Name).SingleOrDefault();

                if (association == null)
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Association mapping for property {0} of class {1} is not defined.", thisSideAssociationProperty.Name, typeof(T).FullName));
                }

                // find all possible associated entities
                Type associatedType = thisSideAssociationProperty.PropertyType;

                if (!association.IsForeignKey)
                {
                    // there is a collection on this side, get collection item type
                    Type listInterface = associatedType.GetInterfaces().Where(i => (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList <>))).SingleOrDefault();

                    if (listInterface == null)
                    {
                        throw new InvalidOperationException("Non-FK association property type must implement IList<T> interface");
                    }

                    associatedType = listInterface.GetGenericArguments().Single();

                    // fill with empty lists
                    foreach (T entity in entities)
                    {
                        object localEntity = entity;
                        association.ThisMember.StorageAccessor.SetBoxedValue(ref localEntity, Activator.CreateInstance(association.ThisMember.Type));
                    }
                }

                // first list to join
                Expression allTsConst = Expression.Constant(entities.AsQueryable());

                // second list to join
                var allAssociatedEntitiesQuery = this.All(associatedType, loadOptions);

                // appying Filter expressions if present
                if (loadOption.LoadOption.Association != null)
                {
                    var allAssociatedEntitiesFilteredEnumerableExpression = new ExpressionReplacer(loadOption.LoadOption.Member.Body, allAssociatedEntitiesQuery.Expression).Visit(loadOption.LoadOption.Association.Body);

                    var asQueryableGenericMethod = TypeSystem.FindQueryableMethod("AsQueryable", new[] { typeof(IEnumerable <>).MakeGenericType(associatedType) }, new[] { associatedType });

                    var allAssociatedEntitiesFilteredQueryableExpression = Expression.Call(asQueryableGenericMethod, allAssociatedEntitiesFilteredEnumerableExpression);

                    allAssociatedEntitiesQuery = allAssociatedEntitiesQuery.Provider.CreateQuery(allAssociatedEntitiesFilteredQueryableExpression);
                }

                Expression allAssociatedEntitiesExpression = allAssociatedEntitiesQuery.Expression;

                // outerKeySelector
                var paramA1 = Expression.Parameter(typeof(T), "a");
                MemberExpression thisKeyPropertyExpression = paramA1.Property(association.ThisKey.First().Member as PropertyInfo);

                // innerKeySelector
                var paramB1 = Expression.Parameter(associatedType, "b");
                MemberExpression otherKeyPropertyExpression = paramB1.Property(association.OtherKey.First().Member as PropertyInfo);

                if (thisKeyPropertyExpression.Type.IsGenericType && thisKeyPropertyExpression.Type.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    var nullableFilterParamA1 = Expression.Parameter(typeof(T), "a");

                    MemberExpression nullableFilterPropertyExpression =
                        nullableFilterParamA1
                        .Property(association.ThisKey.First().Member as PropertyInfo)
                        .Property("HasValue");

                    LambdaExpression whereHasValueFilter = nullableFilterParamA1.ToLambda(nullableFilterPropertyExpression);

                    allTsConst = allTsConst.Where(whereHasValueFilter);

                    thisKeyPropertyExpression = thisKeyPropertyExpression.Property("Value");
                }

                if (otherKeyPropertyExpression.Type.IsGenericType && otherKeyPropertyExpression.Type.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    var nullableFilterParamB1 = Expression.Parameter(associatedType, "b");

                    MemberExpression nullableFilterPropertyExpression =
                        nullableFilterParamB1
                        .Property(association.OtherKey.First().Member as PropertyInfo)
                        .Property("HasValue");

                    LambdaExpression whereHasValueFilter = nullableFilterParamB1.ToLambda(nullableFilterPropertyExpression);

                    allAssociatedEntitiesExpression = allAssociatedEntitiesExpression.Where(whereHasValueFilter);

                    otherKeyPropertyExpression = otherKeyPropertyExpression.Property("Value");
                }

                var outerKeySelector = Expression.Quote(paramA1.ToLambda(thisKeyPropertyExpression));
                var innerKeySelector = Expression.Quote(paramB1.ToLambda(otherKeyPropertyExpression));

                var keyValueType = typeof(KeyValuePair <,>).MakeGenericType(typeof(T), associatedType);

                // resultSelector
                var paramA2        = Expression.Parameter(typeof(T), "a");
                var paramB2        = Expression.Parameter(associatedType, "b");
                var resultSelector = Expression.Quote(
                    Expression.Lambda(
                        Expression.New(
                            keyValueType.GetConstructors().Single(),
                            paramA2,
                            paramB2),
                        paramA2,
                        paramB2));

                var joinMethod = typeof(Queryable).GetMethods().Where(m => m.Name == "Join" && m.GetParameters().Count() == 5).Single();

                var joinMethodGeneric = joinMethod.MakeGenericMethod(typeof(T), associatedType, thisKeyPropertyExpression.Type, keyValueType);

                // join!
                var joinExpression = Expression.Call(null, joinMethodGeneric, allTsConst, allAssociatedEntitiesExpression, outerKeySelector, innerKeySelector, resultSelector);

                var joinResult = allAssociatedEntitiesQuery.Provider.Execute(joinExpression) as IEnumerable;

                var keyProperty   = keyValueType.GetProperty("Key");
                var valueProperty = keyValueType.GetProperty("Value");

                foreach (var pair in joinResult)
                {
                    var thisObject  = keyProperty.GetValue(pair, new object[0]);
                    var otherObject = valueProperty.GetValue(pair, new object[0]);

                    if (association.IsForeignKey)
                    {
                        association.ThisMember.StorageAccessor.SetBoxedValue(
                            ref thisObject,
                            otherObject);
                    }
                    else
                    {
                        var list = association.ThisMember.StorageAccessor.GetBoxedValue(thisObject);
                        list.GetType().GetMethod("Add").Invoke(list, new[] { otherObject });
                    }
                }
            }
        }