1

I have an Interface IBasicData that implements IVeryBasicData for users information (inherited code):

public interface IBasicData:IVeryBasicData
{
    byte[] Password { get; set; }
    string Email { get; set; }
}

public interface IVeryBasicData:IUser
{
    string Name { get; set; }
    string UserId { get; set; }
    string Description { get; set; }
    string Url { get; set; }
}
public interface IUser
{
    DateTime CreationTime { get; set; }
    string PartitionKey { get; set; }
    string RowKey { get; set; }
}

Then I have a method GetUsers from UsersDataSource that returns an IQueryable<IBasicData>, I want to be able to use this IQueryable as the model for a View. But when I try to do it an Exception comes out: the properties cannot be found when calling them in @Hml.DisplayNameFor(model => model.UserId) for example.

So, I've come with this solution:

foreach (var user in usersDataSource.GetUsers())
        {
            var addUser = new UserViewModel()
            {
                CreationTime = user.CreationTime,
                Description = user.Description,
                Email = user.Email,
                Name = user.Name,
                PartitionKey = user.PartitionKey,
                Password = user.Password,
                RowKey = user.RowKey,
                Url = user.Url,
                UserId = user.UserId
            };
            usersViewModel.Add(addUser);
        }
        return View(usersViewModel);

UserViewModel is a class implementing IBasicData. This works, but seems rather ugly to me. Is there a better way to be able to use an IQuaryable<IBasicData> as the View model?

4
  • 2
    I would assume you were getting the exception because you were using an IQueryable<> and disposing your data context before returning the View from your controller. Did you try to return an IEnumberable<> and call .ToList() on your returned entity set? Commented Jul 15, 2013 at 22:16
  • I think I'm not quite getting what you're saying (almost new on this field). Currently, my controller: public ActionResult Index() { return View(users.GetUsers().ToList()); } Commented Jul 15, 2013 at 22:21
  • IQueryable<> is a set of objects that may or may not have been retrieved from the database yet. In EF and LINQ, execution is deferred until you actually begin enumerate the data or cast it to an object such as a list. In your case, the query was most likely being deferred until your view started to render the HTML, but at that time, you had already closed/disposed of your connection to the database. Thus, the exception is being thrown. In my answer below, you will note that I call .ToList() to ensure I have the data before MVC begins to render the view. Commented Jul 15, 2013 at 22:23
  • Are you sure the exception isn't being thrown because the model binder, operating on objects of interface IBasicData, only knows about the Password and Email properties - the model binder probably uses reflection and is maybe not picking up the base interfaces?To be honest I'd recommend using a single class that implements all of the 3 interfaces, and then do a projection from your actual EF entity into an instance of this class (i.e in this case as interface inheritance is tricky at best... Commented Jul 15, 2013 at 23:34

2 Answers 2

1

Update your view to take an IEnumerable in the model declaration first.

View

@model IEnumerable<IBasicData>

@{foreach(var user in Model){
     //I don't know how you are wanting to render out the data...example only
     @user.Email<br/>
     @user.Name
}}

Then, in your controller, you should be able to do the following:

var modelData = usersDataSource.GetUsers().Select(user=>new UserViewModel{
                CreationTime = user.CreationTime,
                Description = user.Description,
                Email = user.Email,
                Name = user.Name,
                PartitionKey = user.PartitionKey,
                Password = user.Password,
                RowKey = user.RowKey,
                Url = user.Url,
                UserId = user.UserId
            }).ToList();
return View(modelData );

Calling .ToList() causes the query to execute immediately and retrieve the data before going to your View.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for answering, Tommy. This solutions throws an Exception: Constructing or initializing instances of the type gpmData.User.UserViewModel with the expression user.CreationTime is not supported.. And still uses manual property asignment, copying one entity to another. What I want is to avoid this.
I wonder if this has to do with you using Interfaces and not concrete classes as your View Model. I have never used interfaces for view model classes (classes with only properties), just concrete classes with inheritance if needed. Do you come from a MVVM background? In a quick search, it appears that to use interfaces, you need to have some method to build a concrete class for them, which would explain your error.
As for copying one entity to another, you could always look into AutoMapper if you wanted to bind a object model to a view model. automapper.org
Yup, MVVM background on me :). Guess I'll stick to a class then, though I don't know why MVC doesn't support Interfaces as Models well, I'd guess it's because you may have 2 interfaces with the same property, precedence issue. Thanks Tommy!
1

Are you sure the exception isn't being thrown because the model binder, operating on objects of interface IBasicData, only knows about the Password and Email properties - the model binder probably uses reflection and is maybe not picking up the base interfaces?

Quote from This article on MSDN Magazine, emphasis mine

For example, even though the Microsoft .NET Framework provides excellent support for object-oriented principles, the DefaultModelBinder offers no support for binding to abstract base classes and interfaces.

To be honest I'd recommend having your ViewModel class just explicitly implement all 3 interfaces, and then keep the projection from your actual EF entity into an instance of this class (i.e usersDataSource.Select(u=>new UserViewModel{...}); and then bind on the view with @model IEnumerable

Interface inheritance doesn't sound like the right approach here; I wouldn't worry about the projection code you have, it's pretty normal and bear in mind it's providing a nice separation between your EF layer and your presentation layer (View).

I do agree with Tommy though on the IEnumerable; once you get out of your EF layer, IEnumerable-ise your data right away so as to materialise your data and close the database connection as quickly as possible.

3 Comments

As far as I understand, this is what I did: a class implementing the interfaces and reflection. The Exception is pretty clear: it cannot go deeper in the interfaces and infer the properties. The reflection was made in one of the constructors.
yes you did but your original code specifies @model IEnumerable<IBasic> which is not the same as IEnumerable<ViewModel>...in the former, only IBasic properties are there, in the latter they are all there. The 3 interface implementations on ViewModel need only exist if for some reason you need to pass these objects into your controller/view by interface. If you are executing that projection in your controller, the interfaces are redundant!
Forgot to update, I have an IEnumerable<UserViewModel> since Tommy's ans, and the projection is executing in UserViewModel, is that still redundant?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.