Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

breeze

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.

like image 334
Mike Thibault Avatar asked Aug 08 '14 20:08

Mike Thibault


2 Answers

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;
  });
}
like image 147
Jay Traband Avatar answered Sep 18 '22 16:09

Jay Traband


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

  • My unmapped properties in breeze does not seems to work whith a projection
  • UnMapped property on the Angular/Breeze SPA template

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.

like image 26
Gavin.Paolucci.Kleinow Avatar answered Sep 20 '22 16:09

Gavin.Paolucci.Kleinow