简体   繁体   中英

Entity Framework 6 - One to Optional Relationship

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);
    }

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM