Search code examples
c#linq-to-entitiesentity-framework-6

Assigning Read Only Property to new object in Entity Framework


I have an object that is mapped to the database for Entity Framework. This is a fairly large object and in getting a list of them, I only want a subset of the data.

The problem is that I have two readonly properties that I need that have some significant logic around a TypeId field. Here's an example:

Here is my class that is mapped to Entity

public class MyBigObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int OwnerId { get; set; }
    public string TypeId { get; set; }
    public bool IsFoo 
    { 
        get { /* complicated logic here for checking if TypeId IsFoo */ }
    }
    public bool IsBar 
    { 
        get { /* complicated logic here for checking if TypeId IsBar */ }
    }

    /* snip about 30 other columns */
}

This is all fine and dandy and works great for pulling lists of this object. However, over time, the object has become large and I want to reduce the amount of data that Entity is retrieving. For that reason, we created a new DTO class that is not mapped to Entity.

public class MyBigObjectItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string TypeId { get; set; }
    public bool IsFoo { get; set; }
    public bool IsBar { get; set; }
    public bool HasObject1 { get; set; }
    public bool HasObject2 { get; set; }
}

It's just a very simple container of getters and setters.

Then, for my linq query, I have something like this

public Task<List<MyBigObjectItem>> GetMyBigObjectItemsAsync(int ownerId, CancellationToken cancellationToken = default(CancellationToken))
{
    return (
        from obj in DataContext.MyBigObject
        join object1 in DataContext.SomeObject1 on obj.Id equals object1.ObjectId into object1items
        join object2 in DataContext.SomeObject2 on obj.Id equals object2.ObjectId into object2items
        where obj.OwnerId = ownerId
        orderby obj.Name
        select new MyBigObjectItem
        {
            Id = obj.Id,
            Name = obj.Name,
            TypeId = obj.TypeId,
            IsFoo = obj.IsFoo,
            IsBar = obj.IsBar,
            HasObject1 = object1items.Any(),
            HasObject2 = object2items.Any()
        }
    ).ToListAsync(cancellationToken);
}

There's quite a lot going on in that query, but I wanted to give an accurate example of what I'm doing. We are joining off to two other objects where we have a one to many relationship. I just need to know if any relationship exists.

The trouble comes with the lines IsFoo = obj.IsFoo, and IsBar = obj.IsBar, where the Intellitrace shows an error:

The specified type member 'IsFoo' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported." (System.NotSupportedException)

Any thoughts on how to make this work, or am I doomed to get the list of full objects and then transform those into my DTO?


Solution

  • ... or am I doomed to get the list of full objects and then transform those into my DTO?

    Pretty much. Anything that is done before evaluating the query has be be something that Entity Framework can translate into SQL, which rules out any custom properties like the ones you created. Your only real alternative is to create a stored procedure that does the logic contained in these properties, returning just the resulting boolean for IsFoo and IsBar, and then use that SP to retrieve your objects. Depending on the complexity of this query, that might be a good move regardless.