Possible issue with Interface support?

Nov 30, 2011 at 11:27 AM

I am playing about with interfaces and custom views and cannot get the views to render the domain objects according to just the metadata defined on the interface.  Instead they render based upon the concrete instance metadata.

I have a domain object called User. What I want to do is to be able to create 3 interfaces that User implements and have the views render the User based on the interface:

IUserTableView - used when displaying a User in a table, so has a restricted subset of all of the User properties.

IUserPropertyView - used when displaying a User in a property list (readonly), so displays all of the properties.

IUserPropertyEdit - used when editing a User in a property edit list (read/write), so hides all non editable properties (i.e. navigation properties that are collections)

 

Interfaces do seem to be adhered to when used as the return type of a navigation property and also in actions, but the html helpers don't seem to honour them.

Is this a bug?  Is it possible to achieve the above?

Coordinator
Nov 30, 2011 at 2:02 PM

No, it's not a bug. When the framework displays an object, it uses the metadata for the type of object itself -  not the return type of the method/property it came from.

Nor would it be desirable to change this.  Say a Payment object has an Payee property defined by the 'role' interface IPayee, where IPayee is implemented by, say, Supplier and Employee.  If I click on the object in the Payee property, I want to see what it really is (Supplier or Employee) and invoke its behaviours.

One place where the interface is used is in the collection type  -  in order to determine what columns to display. So your IUserTableView ought to work. But if the user then clicks on any of the objects in the table (i.e. rows) you will then see the object as it is, not as the type in the collection.

I don't think there is any way to make your pattern work. I think you'll need to resort to custom views  -  if you really can't live with the default views.  Alternatively you can create transient-only view models, created as needed from the underlying objects  -  though that is not a pattern I like. 

Nov 30, 2011 at 2:40 PM

I started by using custom views, but then found I couldn't easily hide fields in navigation properties that were collections.

I then decided to look at using interfaces, which do allow you to hide fields in collection navigation properties when viewing in as a propertylist.

I then thought it might be possible to achieve exactly what I wanted by creating a strongly typed custom view that used interfaces.  Alas, as you say, it doesn't work.

Would it be possible to extend the htmlhelpers/framework to pass in whether you want the object rendered either as the viewmodel type (IUserTableView) or as the underlying domain object type (User)?

I guess it would require new overloads in both the htmlhelpers and in any framework methods that do the reflection calls.  Perhaps overloads that take bindingflags?

Coordinator
Nov 30, 2011 at 4:04 PM

It might be possible, but I suspect it will be quite hard work. It's not something that we're likely to do - so its up to you if you want to experiment.  I would recommend against  -  once you are into that level of effort you are starting to undo the value of using Naked Objects.  Are you sure you can't live with the views as they are? If not, you could try using viewmodels instead.

Nov 30, 2011 at 4:30 PM

Not a problem, thanks for the responses.

The big thing I wanted to achieve was restricting the columns in the navigation collections which I have been able to do by introducing an interface.

Coordinator
Nov 30, 2011 at 11:36 PM

It has just occurred to me, Paul, that you could probably do everthing you want using CSS alone!  All the properties have individual IDs and you can use nested selectors to detect whether they are being rendered within a table, an object view of an edit and hide them accordingly.  Worth a try, at least.

Dec 1, 2011 at 9:53 AM

I think I had a similar requirement and ended up creating a custom ObjectView.aspx. In this snippet I use a CollectionTableWith function but there's also a CollectionTableWithout you can use:

<%: Html.Menu(Model)%>
<%: Html.PropertyListWithout(Model, x => x.LeaseTerms, x => x.LeaseItems)%>
<div class="Property">
 <label>Lease Items:</label> <%: Html.CollectionTableWith<EstatioModel.LeaseItem>(Model.LeaseItems, x => x.Lease.Code, x => x.Unit.Code, x => x.Type, x => x.StartDate, x => x.EndDate, x => x.ActiveLeaseTermValue)%> </div> <%}%>


Dec 1, 2011 at 12:59 PM

That's a great idea, completely forgot the section on css and the flexibility that you permit.  Thanks.