Question

I'm trying to get data from my Webapi2 Breeze controller with Entity Framework 6 and .NET 4.5.1. And get the error "unable to locate property" when I use the Where clause on a navigation property. The call is not even made to the Webapi2 Controller

If I leave out the where clause, the data is returned correctly.

The relevant part of the c# class:

public class NotificationRule {
    public Guid NotificationRuleId { get; set; }
    public virtual NotificationRuleSet NotificationRuleSet { get; set; }
}

The relevant part of the C# class in the navigational property NotificationRuleSet:

public class NotificationRuleSet{
    public Guid NotificationRuleSetId { get; set; }
    public virtual List<NotificationRule> NotificationRules { get; set; }
}

The relevant part of the C# Breeze controller:

    public IQueryable<NotificationRuleSet> NotificationRuleSets()
    {
        return _contextProvider.Context.NotificationRuleSets;
    }

    public IQueryable<NotificationRule> NotificationRules()
    {
        return _contextProvider.Context.NotificationRules;
    }

The relevant part of the Query (Typescript):

            var query = breeze.EntityQuery.from("NotificationRules")
                .where ("NotificationRuleSet.NotificationRuleSetId","==", this.NotificationRuleSetId)
                .expand("NotificationRuleSet");


            var Result = this.BreezeEntityManager
                .executeQuery(query)
                .then((data) => this.RefreshViewModelCallback(data))
                .fail((data) => alert("Fail to retrieve data"));

If I leave the Where clause out, the data is transferred correctly as you can see in this Fiddler dump:

{
    "$id": "1",
    "$type": "Imp.Classes.NotificationRule, Imp",
    "NotificationRuleId": "11111111-be1e-423c-ac5b-f2c689093aca",
    "NotificationRuleSet": {
        "$id": "2",
        "$type": "Imp.Classes.NotificationRuleSet, Imp",
        "NotificationRuleSetId": "11111111-1bd6-4520-9f69-381504b8e2b2",
        "NotificationRules": [
            {
                "$ref": "1"
            }
        ],
    },
 }

So I get an error that a property does not exists, but it seems to exists.

Using a Where on a non navigational property works fine.

I've read something about camelCasing but replacing NotificationRuleSet with notificationRuleSet gives the same error.

EDIT: The solutions seems to be that NotificationRules in the Viewmodels query should start with a lowercase character, regardless wether the first character of the controllers method name is upper or lowercase .

Was it helpful?

Solution

camelCasing is most likely your issue provided both the entity and property do exist -

.where('notificationRuleSet.notificationRuleSetId', "==", this.NotificationRuleSetId)

Remember that when you are camelCasing your property names it is for the navigation property as well.

OTHER TIPS

I thought I had an explanation after reviewing you interaction with PW Kad.

My guess was that the ACTUAL defaultResourceName for your NotificationRule type is "notificationRules".

Can you tell me what it is? The following expression will reveal it:

manager.metadataStore.getEntityType('NotificationRule').defaultResourceName;

Another question. You say it fails. What is the failure exception (check the payload of the response). Is it something like this?

$id: "1",
$type: "System.Web.Http.HttpError, System.Web.Http",
Message: "The query specified in the URI is not valid.",
ExceptionMessage: "A binary operator with incompatible types was detected. Found operand types 'Edm.Guid' and 'Edm.String' for operator kind 'Equal'.",
ExceptionType: "Microsoft.Data.OData.ODataException",

Here is what I was thinking. Most of the time, Breeze doesn't need to know the root type of a query when that query is sent to the server. It can simply wait for the query results and reason over the JSON to determine the type (or types) involved.

But the occasional query involves a filter comparison that is ambiguous in its data type. GUIDs are a good example. Unless breeze knows the query root type it can't know for sure if the "11111111-be1e-423c-ac5b-f2c689093aca" in "foo == '11111111-be1e-423c-ac5b-f2c689093aca'" should be a string or a GUID. A human would guess it's a GUID; Breeze is not so sure. You can be sure only if you know the datatype of the "foo" property.

Breeze will compose the query anyway. If it guesses string if produces a URL that looks like "...foo eq '11111111-be1e-423c-ac5b-f2c689093aca'..." and that will fail (for me anyway).

I thought this could be your issue.

I tried an experiment in DocCode that I thought would demonstrate it. I changed the endpoint name for an Order query to something that is NOT the Order type's defaultResourceName(which is "Orders").

As it happens, Web API doesn't care if the URL says 'orders' or 'Orders' so I can achieve my goal of confusing Breeze about the root type by pointing the query to "orders" and the query should still be routed by Web API to the proper controller GET method.

I was expecting that Breeze would compose the GUID query as a string and thus I could duplicate your issue. Here is my attempt

/*********************************************************
* Orders of Customers with Alfred's ID
* Customer is the related parent of Order
* CustomerID is a GUID
* Demonstrates "nested query", filtering on a related entity
* where the filter criteria is ambiguous (string or GUID)
* and only knowing the root query type (Order) can disambiguate.
* The 'defaultResourceName' for Order is "Orders", not "orders"
* so I expect this test to fail ... but it doesn't ...
* because Breeze disambiguated anyway. 
*********************************************************/
test("orders of Customers w/ Alfred's ID (resource name is lowercased)", 2, function () {

    var query = EntityQuery.from("orders")
        .where("Customer.CustomerID", "==", testFns.wellKnownData.alfredsID)
        .expand("Customer");

    verifyQuery(newEm, query, "orders query", showOrdersToAlfred);
});

Unfortunately, the test passes!

This is the URL that Breeze sent to the server:

http://localhost:56337/breeze/Northwind/orders?$filter=Customer.CustomerID eq guid'785efa04-cbf2-4dd7-a7de-083ee17b6ad2'&$expand=Customer

DAMN Breeze (v.1.4.12) was too smart for me. It somehow figured out that my comparison value is a GUID ... despite not knowing the root type of the query.

That means I do not have an explanation for why, in your example, breeze.EntityQuery.from("notificationRules") works but breeze.EntityQuery.from("NotificationRules") does not.


Maybe I'll have another idea once you tell us the defaultResourceName AND show us the URLs that are generated (a) when it works and (b) when it does not work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top