danielrozo
danielrozo

Reputation: 1442

Interface implementing interface as a View model in ASP.NET MVC 4

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?

Upvotes: 1

Views: 9028

Answers (2)

Stephen Byrne
Stephen Byrne

Reputation: 7485

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.

Upvotes: 1

Tommy
Tommy

Reputation: 39807

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.

Upvotes: 1

Related Questions