Admir Tuzović
Admir Tuzović

Reputation: 11177

Linq to entities and generics - unable to cast

I have two interfaces for users:

public interface IUserModel
{
    int UserId {get;set;}
}

public interface IUserAuthModel : IUserModel
{
    string Username {get;set;}
    string Password {get;set;}
}

I have user model that implements IUserAuthModel because it requires authorization check for access:

public class UserSubscriptionModel : IUserAuthModel
{
    public int UserId {get;set;}
    public string Username {get;set;}
    public string Password {get;set;}
    public bool Subscribed {get;set;}
}

I have user repository based on EF 4.3.1 where I have method for projection:

IQueryable<T> ProjectTo<T>() where T: IUserModel
{
    if(typeof(T) == typeof(UserLoginModel)
    {
        return db.Users.Select(x => new UserSubscriptionModel {
        UserId = x.UserId,
        Username = x.Username,
        Password = x.Password,
        Subscribed = x.Subscribed
    }) as IQueryable<T>;
}

I have method that retrieves one user based on conditional expression:

T Get<T>(conditionalexpression) where T : IUserModel
{
    return ProjectTo<T>.Where(conditionalexpression).FirstOrDefault();
}

I'm implementing authorization method:

public bool Authorize<T>(string username, string password, out T TUser) where T : IUserAuthModel
{
    TUser = Get<T>(x => x.Username == username && x.Password == password);
     ... some other irrelevant code
}

And then I do the following:

UserSubscriptionModel model;
bool authorized = Authorize<UserSubscriptionModel>("hello","world", out model);

This code fails on part where it tries to extract FirstOrDefault. It says Linq to Entities supports casting primitive types.... can't cast from UserSubscriptionModel to IUserAuthModel - or other way around, can't remember. But point is, my generics are not working, even though IUserAuthModel inherits from IUserModel so if my class implements IUserAuthModel it implements IUserModel as well.

What am I missing? I'm not getting a single error/warning and I've made sure my inheritance is done properly (or at least I think so).

I'm sorry if code has some typos, I left real code at work.

Thanks for all the tips.

Upvotes: 1

Views: 664

Answers (2)

Paul d&#39;Aoust
Paul d&#39;Aoust

Reputation: 3209

Entity Framework needs to know that the generic parameter that you're projecting to can be a plain ol' object or struct of some type, otherwise it'll only be able to infer it as an IUserModel. If you add another type constraint telling it T will always be one of those things (depending on your domain model; you're probably using classes though):

IQueryable<T> ProjectTo<T>() where T: class, IUserModel

(or this, if all your IUserModels are structs:)

IQueryable<T> ProjectTo<T>() where T: struct, IUserModel

that exception should go away.

See these two topics, where I found the answer:

https://stackoverflow.com/a/13701650/183350

https://stackoverflow.com/a/19847671/183350

Upvotes: 2

Honorable Chow
Honorable Chow

Reputation: 3153

instead of return as IQueryable try the linq Cast<>

IQueryable<T> ProjectTo<T>() where T: IUserModel
{
    if(typeof(T) == typeof(UserLoginModel)
    {
        return db.Users.Select(x => new UserSubscriptionModel {
        UserId = x.UserId,
        Username = x.Username,
        Password = x.Password,
        Subscribed = x.Subscribed
    }).Cast<T>;
}

Upvotes: 0

Related Questions