简体   繁体   中英

Cast Entity to Implemented Interface in a Generic Method Using LINQ for Entity Framework

I have a generic method to query objects of type TEntity in EF. I Want to add a condition as a where clause if TEntity implements a specific interface. The method I have is:

public TEntity GetByUserID(Guid userID)
{
    var query = this.DbSet;
    if (typeof (TEntity).IsImplementationOf<IDeletableEntity>())
    {
        query = query
            .Where((x => !((IDeletableEntity)x).IsDeleted);
    }
    return query
        .FirstOrDefault(x => x.UserID == userID);
}

IsImplementationOf<>() is method which just returns true/false as the name implies.

When I run this for the entity Address which implement IDeletableEntity, I get an error:

Unable to cast the type 'Address' to type 'IDeletableEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.

Any ideas how I can go around this limitation of LINQ?

This is a working solution:

public TEntity GetByUserID(Guid userID, params Include<TEntity>[] includes)
{
    var query = this.DbSet;
    query = Where<IDeletableEntity>(query, x => !x.IsDeleted);
    return query
        .FirstOrDefault(x => x.UserID == userID);
}

public static IQueryable<TEntity> Where<TPredicateWellKnownType>(IQueryable<TEntity> query, Expression<Func<TPredicateWellKnownType, bool>> predicate)
{
    if (typeof(TEntity).IsImplementationOf<TPredicateWellKnownType>())
    {
        query = ((IQueryable<TPredicateWellKnownType>)query)
            .Where(predicate)
            .Cast<TEntity>();
    }
    return query;
}

If all DbSets has the 'UserID' property then create another interface named 'IUserID' and then try this code:

    protected TEntity GetByUserID<TEntity>(Guid userID) where TEntity : class
    {
        var user = this.Set<TEntity>()
            .ToList()
            .Cast<IDeletableEntity>()
            .Where(u => (!u.IsDeleted))
            .Cast<IUserID>()
            .Where(u => (u.UserID == userID))
            .FirstOrDefault();
        return user;
    }

I think the problem might be the direct cast you are doing in your statement as well as query might be implementing enumeration types and so IDeletable is not implemented as on one entity.

LINQ-to-entities casting issue

proposed this solution.

 return query.ToList()
                       .Cast<IDeletable>()
                       .Where( e => e.Deleted )
                       .Cast<T>();

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