Error querying with navigation properties?

I have the following entity class:

using System;
using System.Collections.Generic;
using Facebook.Business.Domain.Accounts;

namespace Business.Domain.Posts
{
    /// <summary>
    /// This class defines a post.
    /// </summary>
    public class Post : Entity<Post>
    {
        #region Fields

        private UserAccount _author;
        private UserAccount _owner;
        private DateTime _date;
        private string _text;
        private IList<Comment> _comments; 

        #endregion

        #region Ctors

        protected Post()
        {
        }

        public Post(UserAccount author,
                    UserAccount owner,
                    DateTime date,
                    string text)
        {
            _author = author;
            _owner = owner;
            _date = date;
            _text = text;
            _comments = new List<Comment>();
        }

        #endregion

        /// <summary>
        /// Gets or sets the account that writes and sends the current post.
        /// </summary>
        public virtual UserAccount Author
        {
            get { return _author; }
            protected set { _author = value; }
        }

        /// <summary>
        /// Gets or sets the datetime of the current post.
        /// </summary>
        public virtual DateTime Date
        {
            get { return _date; }
            protected set { _date = value; }
        }

        /// <summary>
        /// Gets or sets the text of the current post.
        /// </summary>
        public virtual string Text
        {
            get { return _text; }
            protected set { _text = value; }
        }

        /// <summary>
        /// Gets or sets the user that receives the current post.
        /// </summary>
        public virtual UserAccount Owner
        {
            get { return _owner; }
            protected set { _owner = value; }
        }

        /// <summary>
        /// Gets or sets the comments for the current post.
        /// </summary>
        public virtual IList<Comment> Comments
        {
            get { return _comments; }
            protected set { _comments = value; }
        }

        public void Add(Comment comment)
        {
            _comments.Add(comment);
        }

        #region Implementation of ICommentable

        IEnumerable<Comment> ICommentable.Comments { get { return Comments; } }

        #endregion
    }
}

the class UserAccount know nothing about the Post class, UserAccount is also an entity.

then I need to implement this method in the respective repository class:

/// <summary>
/// Retrieve the posts that were posted by a given account: <paramrefname="author"/>
/// </summary>
/// <param name="author">An account.</param>
/// <returns>The posts that were posted by <paramref name="author"/></returns>
public IQueryable<Post> FindPostsFrom(UserAccount author)
{
    ContractUtil.NotNull(author);

    return Set.Where(post => post.Author.Equals(author));
}

but when I run the tests I obtain the following error:

System.NotSupportedException : Unable to create a constant value of type 'Facebook.Business.Domain.Accounts.UserAccount'. Only primitive types or enumeration types are supported in this context.

... and the stack ...

at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, ref DbExpression source, ref DbExpressionBinding sourceBinding, ref DbExpression lambda) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.Convert() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1 forMergeOption) at System.Data.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) at System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Linq.Enumerable.First(IEnumerable1 source) at System.Linq.Queryable.First(IQueryable1 source) at Data.EntityFramework.Tests.PostRepositoryTests.FindPostsFrom_FindingAPostGivenAnAuthor_ShouldRetrieveIt() in PostRepositoryTests.cs: line 136

So far, Can anyone explain me what it is going on here ? Seems to be that I should not use navigation properties to queries.

Can anyone give me an alternative solution ?

Thanks in advance.

Upvotes: 1

Views: 150

Answers (2)

Gert Arnold
Gert Arnold

Reputation: 109255

In Entity Framework queries should always use primitive properties in predicates. So you could do

Set.Where(post => post.Author.Id == author.Id);

Or (as dutzu suggests) expose AuthorId as a property of Post. If you do that, you'll have to tell EF that AuthorId and Author are both part of the same foreign key. It depends on the API you use (ObjectContext or DbContext) how to do that.

A reason for EF to be so strict (there may be more) is that it can not make any assumptions about equality of value types or classes. Since EF allows working with POCO's, Equals() or the == operator will not automatically evaluate equality of key values. The POCO will have to explicitly implement IEquatable to ensure equality by key. Equality of primitive values on the other hand, is always defined.

Upvotes: 1

dutzu
dutzu

Reputation: 3920

Being that they are related entities and the relationship is one-to-many (one author has many posts) that would mean that each post has a field which retains the value of the authorId.

You could rewrite the query to search for posts where post.authorId = author.Id.

Upvotes: 1

Related Questions