0

I am fairly new to EF and using version 6 and am stuck on a one-to-none relationship. My overall structure is I have a Gallery object which has an ICollection of GalleryMediaItem entities inside it and each GalleryMediaItem has a MediaItem entity associated with it. Note that a GalleryMediaItem entity has a requirement that the underlying MediaItem has a value, though MediaItem entities themselves have no knowledge of a GalleryMediaItem, something that is intentional because MediaItem entities could tie to other things besides a GalleryMediaItem. Here's an example:

Gallery
    ---> ICollection<GalleryMediaItems>
             ---> [other properties]
             ---> MediaItem [has no knowledge of GalleryMediaItem]

What I am trying to do is to get lazy loading to work for MediaItem entities when I access the MediaItem property off of a GalleryMediaItem instance. My database structure matches the code structure above (GalleryMediaItem has a GalleryId and a MediaItemId, but MediaItem does NOT have a GalleryMediaItemId, nor should it) and the returned object is consistently null. The odd part here is the MediaItemId property on my GalleryMediaItem instance is populated correctly, but the navigation property itself won't work. I've tried every variant of the following with no luck:

HasRequired(p => p.MediaItem).WithOptional();
HasRequired(p => p.MediaItem).WithMany();

And so on. I don't get any errors with every variant I've tried, just no navigation property access.

What is the proper way to configure this where GalleryMediaItem can fill in a child MediaItem entity, but MediaItem by itself (both via POCO and in the database) has no knowledge of a parent owner?

EDIT

One of the comments asked that I show the code being used to retrieve individual items. I have a generic framework sitting on top of my DbContext instances, but underneath this is the method for grabbing a single Gallery entity:

    /// <summary>
    /// Retrieves an entity based on the Id parameter.
    /// </summary>
    /// <typeparam name="E">The type of BaseEntity being retrieved.</typeparam>
    /// <param name="Id">The Id to retrieve.</param>
    /// <returns>A BaseEntity of type E if a match is found, otherwise null.</returns>
    public virtual E GetById<E>(long Id)
        where E : BaseEntity
    {
        var whereClause = DataUtilities.BuildOrExpressionTree<E, long>(new long[] { Id }, entity => entity.Id);
        IQueryable<E> entities = this.GetTable<E>().Where(whereClause);

        if (entities.Count() == 1)
            return entities.First();
        else
            return null;
    }

The BaseEntity generic above is just a super basic POCO that has things like Id, CreateDate, MaintDate, etc. on it. None of my POCO classes use attributes for their corresponding DB values and everything is done via the fluid EF API. Gallery directly inherits BaseEntity so this generic functions as expected.

The DataUtilities.BuildOrExpressionTree function looks like this:

    public static Expression<Func<TValue, bool>> BuildOrExpressionTree<TValue, TCompareAgainst>(IEnumerable<TCompareAgainst> list, Expression<Func<TValue, TCompareAgainst>> convertBetweenTypes)
    {
        ParameterExpression inputParam = convertBetweenTypes.Parameters[0];
        var binaryExpressionTree = BuildBinaryOrTree(list.GetEnumerator(), convertBetweenTypes.Body, null);
        return Expression.Lambda<Func<TValue, bool>>(binaryExpressionTree, new [] { inputParam });
    }

    private static Expression BuildBinaryOrTree<T>(IEnumerator<T> itemEnumerator, Expression expressionToCompareTo, Expression expression)
    {
        if (itemEnumerator.MoveNext() == false)
            return expression;

        ConstantExpression constant = Expression.Constant(itemEnumerator.Current, typeof(T));
        BinaryExpression comparison = Expression.Equal(expressionToCompareTo, constant);

        BinaryExpression newExpression;

        if (expression == null)
            newExpression = comparison;
        else
            newExpression = Expression.OrElse(expression, comparison);

        return BuildBinaryOrTree(itemEnumerator, expressionToCompareTo, newExpression);
    }
Scott Salyer
  • 2,165
  • 7
  • 45
  • 82

1 Answers1

0

If you use

HasRequired(p => p.MediaItem).WithMany();

you should show foreignkey property:

HasRequired(p => p.MediaItem).WithMany().HasForeignKey(p=>p.MediaItemId);

In HasRequired(p => p.MediaItem).WithOptional(); you should also show foreign key, but in this case you can use ForeignKeyAttribute, or method Map.

Kirill Bestemyanov
  • 11,946
  • 2
  • 24
  • 38
  • I tried that one and `MediaItem` still came back null on `GalleryMediaItem`. I think that's what is so confusing - if it had an error, I could fix that, but it's just not doing anything. It's so strange. I have the output from my DbContext going to the Debug window and I can see the query it does and it never hits the MediaItem table at all - just stays at the top level. – Scott Salyer Nov 27 '14 at 01:49
  • could you show me code that you use to get data from database – Kirill Bestemyanov Nov 27 '14 at 06:11
  • I updated the code above to show it - it'd be ugly as a comment. – Scott Salyer Nov 30 '14 at 20:45
  • it is not all what i wanted. Are you sure that lazy loading is on? Are you sure that dbcontext is not disposed when you try to lazy load navigation property? Could you use this code instead your framework: dbcontext.Set().Include(c=>c.MediaItem).FirstOrDefault(c=>c.Id==id); and look in navigation property? – Kirill Bestemyanov Nov 30 '14 at 21:14
  • I tried that, but it won't even build - it says `Cannot convert lambda expression to type 'string' because it is not a delegate type.` Lazy loading is definitely on because I can get other items and then drill down into each item - when I use the hovering menu next to an item (in debug) I can see those objects as a `DynamicProxy` object that, when expanded, is my real entity. – Scott Salyer Nov 30 '14 at 22:47