How can I retrieve a calculated property without expanding to related navigation properties?
Asked Answered
G

2

6

I followed the great advice here (Handling calculated properties with breezejs and web api) to allow Breeze to access my calculated properties which I have set up in a partial class on the server side:

    public partial class EventPerson
{
    [NotMapped]
    public Decimal TotalAmountPaid
    {
        get
        {
            return this.EventPersonPayments.Sum(p => p.AmtPaid);
        }
    }
}

But for each EventPerson I retrieve, this value shows up as 0 unless I use .expand("EventPersonPayments") clientside or .Include("EventPersonPayments") serverside.

I don't want all the data in EventPersonPayments to be serialized and sent to the client; all I want is the summed value. Is this possible?

EDIT: If my calculated property is derived from other properties already in the entity, it works fine. For example:

    public partial class EventPerson
{
    [NotMapped]
    public String DisplayName
    {
        get
        {
            return this.FirstName + " " + this.LastName;
        }
    }
}

returns the DisplayName in the JSON payload. The former type of calculated property always returns 0 or null unless I specifically load all the extra information.

I considered converting these into User Defined Functions in SQL Server, but I shouldn't have to throw out my C# code just to make it work the way it should.

Gerson answered 8/8, 2014 at 20:15 Comment(2)
Just did a calculation and my JSON payload would be 1/5 the size if I didn't need to bring positively everything over just to get the calculated values I need. Anyone have any ideas?Gerson
I'm a bit surprised others don't seem to have this problem as well; I know summing is something Breeze JS is supposedly working on for a future release, but even if they do, calculated properties based on navigation properties would still be an important capability. Are others approaching this problem differently somehow?Gerson
I
3

One approach is to use a projection that incorporates both the entities being queried and some calculated properties as well. i.e. your server query might look like this:

[HttpGet]
public IQueryable<Object> CustomersAndFreightTotals(companyName) {
  var stuff = ContextProvider.Context.Customers
    .Where(c => c.CompanyName.StartsWith(companyName))
    .Select(c => new { Customer = c, FreightTotal = c.Orders.Sum(o => o.Freight)) });
  return stuff;
}

This query will load all of your customers that start with a specified company name but will also give you the "total freight" for all of the orders on each customer.

You would call this with code something like this:

var query = EntityQuery.from("CustomersAndFreightTotals")
    .withParameters({ companyName: "C" });
myEntityManager.executeQuery(query).then(function(data) {
  var results = data.results;
  results.forEach(function (r) {
    // note that each customer WILL also be added to the local entityManager
    // because it is an entity, whereas the freightTotal is only available here.
    var customer = r.Customer;  
    var freightTotal = r.FreightTotal;
    // and if you wanted to hack the customer entity
    // you could do this.
    customer.freightTotal = freightTotal;
  });
}
Idiosyncrasy answered 20/11, 2014 at 18:55 Comment(2)
Then just leave "Customer" out of the projection or only include those properties of customer that you are interested in. i.e. .Select( CompanyName = c.CompanyName, FreightTotal = ... )Idiosyncrasy
This does work, but it's not without its own problems. I would need to move all my logic from all of my partial classes into the query itself; if it's in the query, I can't utilize it in other serverside code. Additionally, I can't .expand() from Breeze on a method that returns an Object.Gerson
S
2

I came across this problem also, and there are a couple of other questions/answers that seem to point to what's going on:

From my understanding, to put it shortly, [NotMapped] prevents Breeze/Entity Framework from correctly wiring up to the field. Yet Json.NET will serialize the field and send it to Breeze, which will populate the field if you've manually set it up via the class's constructor, and the data has been retrieved by using expand for the other property which Entity Framework recognizes. This seems to be almost an accident you can get [NotMapped] fields to work on the client in this given case; the Breeze+Entity Framework does not seem to be designed for this case.

There is a suggestion at Breeze's User Voice that you could vote and comment on. I'm not sure that Breeze could solve this problem themselves without some work from the Entity Framework team, but at least it could put the issue on their radar.

Stemson answered 20/10, 2014 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.