tags:

views:

130

answers:

1

I'm getting started with FubuMVC and I have a simple Customer -> Order relationship I'm trying to display using nested partials. My domain objects are as follows:

public class Customer
{
    private readonly IList<Order> orders = new List<Order>();

    public string Name { get; set; }
    public IEnumerable<Order> Orders
    {
        get { return orders; }
    }

    public void AddOrder(Order order)
    {
        orders.Add(order);
    }
}

public class Order
{
    public string Reference { get; set; }
}

I have the following controller classes:

public class CustomersController
{
    public IndexViewModel Index(IndexInputModel inputModel)
    {
        var customer1 = new Customer { Name = "John Smith" };
        customer1.AddOrder(new Order { Reference = "ABC123" });

        return new IndexViewModel { Customers = new[] { customer1 } };
    }
}

public class IndexInputModel
{
}

public class IndexViewModel
{
    public IEnumerable<Customer> Customers { get; set; }
}

public class IndexView : FubuPage<IndexViewModel>
{
}

public class CustomerPartial : FubuControl<Customer>
{
}

public class OrderPartial : FubuControl<Order>
{
}

IndexView.aspx: (standard html stuff trimmed)

<div>
    <%= this.PartialForEach(x => x.Customers).Using<CustomerPartial>() %>
</div>

CustomerPartial.ascx:

<%@ Control Language="C#" Inherits="FubuDemo.Controllers.Customers.CustomerPartial" %>
<div>
    Customer
    Name: <%= this.DisplayFor(x => x.Name) %> <br />    
    Orders: (<%= Model.Orders.Count() %>) <br />

    <%= this.PartialForEach(x => x.Orders).Using<OrderPartial>() %>
</div>

OrderPartial.ascx:

<%@ Control Language="C#" Inherits="FubuDemo.Controllers.Customers.OrderPartial" %>
<div>
    Order <br />
    Ref: <%= this.DisplayFor(x => x.Reference) %>
</div>

When I view Customers/Index, I see the following:

Customers 
Customer Name: John Smith 
Orders: (1) 

It seems that in CustomerPartial.ascx, doing Model.Orders.Count() correctly picks up that 1 order exists. However PartialForEach(x => x.Orders) does not, as nothing is rendered for the order. If I set a breakpoint on the Order constructor, I see that it initially gets called by the Index method on CustomersController, but then it gets called by FubuMVC.Core.Models.StandardModelBinder.Bind, so it is getting re-instantiated by FubuMVC and losing the content of the Orders collection.

This isn't quite what I'd expect, I would think that PartialForEach would just pass the domain object directly into the partial. Am I missing the point somewhere? What is the 'correct' way to achieve this kind of result in Fubu?

Update: In case it helps, this is the top few lines of the stacktrace the first time the Order constructor gets hit:

at FubuDemo.Customer..ctor()
at FubuDemo.Controllers.Customers.CustomersController.Index(IndexInputModel inputModel)
at lambda_method(ExecutionScope , CustomersController , IndexInputModel )
at FubuMVC.Core.Behaviors.OneInOneOutActionInvoker`3.performInvoke()

And the second time:

at FubuDemo.Customer..ctor()
at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at FubuMVC.Core.Models.StandardModelBinder.Bind(Type type, IBindingContext context)
at FubuMVC.Core.Runtime.ObjectResolver.BindModel(Type type, IBindingContext context)
at FubuMVC.Core.Runtime.ObjectResolver.BindModel(Type type, IRequestData data)
at FubuMVC.Core.Diagnostics.RecordingObjectResolver.BindModel(Type type, IRequestData data)
at FubuMVC.Core.Runtime.FubuRequest.<>c__DisplayClass2.<.ctor>b__0(Type type)
at FubuMVC.Core.Util.Cache`2.Retrieve(KEY key)
at FubuMVC.Core.Util.Cache`2.get_Item(KEY key)
at FubuMVC.Core.Runtime.FubuRequest.Get[T]()
+1  A: 

Jon:

PartialForEach does not new up new models, it passes the models you pass (i.e. x.Orders).

It looks like the problem you're facing is because you did not add ".Using" in your CustomerPartial.ascx

In CustomerPartial.ascx, change

<%= this.PartialForEach(x => x.Orders) %>

to

<%= this.PartialForEach(x => x.Orders).Using<OrderPartial>() %>

chadmyers
Hi chad, thanks for the response, I had the Using in originally but removed it for some reason. After adding it back in I still have the same problem, nothing appears to have changed. I'm still seeing a call to the Order constructor with [External Code] and then StandardModelBinder.Bind immediately following in the stacktrace.
Jon M
I've added some stacktrace detail to the question in case it helps to give an idea of what I'm talking about.
Jon M
Ok, I added an example for this in the "HelloWorld" sample project and am seeing the same behavior. I'm looking into the solution now.
chadmyers
Ok, this bug is fixed:http://github.com/DarthFubuMVC/fubumvc/commit/ef7607796eedb8fe2fe64513bde7550d7da65519thanks for finding this!The problem only showed up when there are NESTED PartialForEach's. There was an errant call to IFubuRequest.Get() for the wrong model and it was not finding it (since it was the wrong model) and so FubuRequest's default behavior is to just new one up for you and model-bind it which ended up returning the wrong model with all null/empty values thus thwarting the partial binding.The fix was to not call FubuRequest at all but to pass all the values down.
chadmyers
I thought I had it set up to pass all values down and NOT do model binding at all in PartialForEach, but I missed one line. Sorry! If you pull the latest commit (linked above) it should work now. I also updated FubuHelloWorld (hit /products if you have the helloworld app running) so demonstrate/verify the fix for this issue. Thanks again!!!
chadmyers
No worries, glad I could help. Just pulled the latest version and all works fine now. Thanks for the incredibly quick fix!
Jon M