RockResolve
RockResolve

Reputation: 1501

Error while querying Breeze with predicate on DateTimeOffset column

I am trying to query my entity with a predicate on a DateTimeOffset column, but getting a 500 error response from the Breeze.WebApi. The error message is

Unable to perform operation: leon types:System.Nullable`1[System.DateTimeOffset], System.DateTime

The uri I see produced is something like

http://localhost:49800/api/Breeze Orders?$filter=LocalDateTime%20le%20datetime'2013-03-03T00%3A00%3A00.000Z'

What is working:

The full error response from the query is:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Unable to perform operation: leon types:System.Nullable`1[System.DateTimeOffset], System.DateTime
</ExceptionMessage>
<ExceptionType>System.Exception</ExceptionType>
<StackTrace>
at Breeze.WebApi.ParseTreeVisitor.CoerceTypes(String operatorName, Expression& leftExpr, Expression& rightExpr) at Breeze.WebApi.ParseTreeVisitor.VisitBinary(ParseTreeNode node, String operatorName, Expression leftExpr, Expression rightExpr) at Breeze.WebApi.ParseTreeVisitor.VisitNode(ParseTreeNode node) at Breeze.WebApi.ParseTreeVisitor.Parse(Type rootType, ParseTreeNode node) at Breeze.WebApi.ExpressionTreeBuilder.Parse(Type rootType, String source) at Breeze.WebApi.ODataActionFilter.BuildFilterFunc(String filterQueryString, Type elementType) at Breeze.WebApi.ODataActionFilter.OnActionExecuted(HttpActionExecutedContext actionExecutedContext) at System.Web.Http.Filters.ActionFilterAttribute.CallOnActionExecuted(HttpActionContext actionContext, HttpResponseMessage response, Exception exception) at System.Web.Http.Filters.ActionFilterAttribute.<>c__DisplayClass2.<System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync>b__0(HttpResponseMessage response) at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass41`2.<Then>b__40(Task`1 t) at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)
</StackTrace>
</Error>

.

EDIT

Getting there, but not quite. After installing Breeze 1.2.8 the client is now generating correctly typed uri e.g.

http://localhost:49800/api/Breeze/Orders?$filter=LocalDateTime%20ge%20datetimeoffset'2013-01-01T11%3A00%3A00.000Z'

In my case using setEntityTypeForResourceName to fix the entity type isnt needed. The above uri still gives me the error:

Unable to perform operation: leon types:System.Nullable`1[System.DateTimeOffset], System.DateTimeOffset

Changing my server side model from

public Nullable<System.DateTimeOffset> LocalDateTime { get; set; }

to

public System.DateTimeOffset LocalDateTime { get; set; }

allows my uri to work. So can we please have a fix for nullable DateTimeOffset.

Upvotes: 1

Views: 833

Answers (1)

Jay Traband
Jay Traband

Reputation: 17052

This is fixed in v 1.2.7. Please post back if you still see the error.

--- Further information ---

There have been several posts since 1.2.7 indicating that this error was still occuring. It turns out that the issue is NOT that breeze doesn't understand 'dataTimeOffsets'. It is that breeze is very forgiving of incorrect EntityType/Resource name mappings in queries except in the case of queries involving 'DateTimeOffsets'. This post is a bit long, so if you just need the fix, read the last paragraph.

The "Unable to perform operation: ... " error occurs when executing a query against a dateTimeOffset property when the resource name declared in the query ( the value in the 'from' clause) cannot be mapped to an existing entityType name (defined in metadata).

If breeze can find an entity type that maps to the resource name declared in the query, it uses the metadata for that entityType to validate the query and create an OData filter string. However, if it can't find a matching entity type, which is a perfectly acceptable condition because some 'resourceName's will not map to entities, it tries to infer the datatypes involved in any query clauses by looking at any constant parameters involved in the query and using their datatype. The problem is that a javascript date object can be mapped to either a .NET 'DateTime' or a 'DateTimeOffset' and Breeze can only pick one and in this case picks the 'wrong' one.

This kind of mismapping does not cause problems when data is returned from a query because breeze only uses the entity type of the query to validate and construct an appropriate OData filter string. Data returned from any query is inspected to determine the entityTypes of each item returned so the resourceName/entityType mapping is not needed. ( A single query may return multiple entity types because of the use of 'expand' or 'select' clauses.)

The root cause of the mapping issue involves the map of resource names to entity types that breeze keeps in each MetadataStore. This mapping is available via the MetadataStore.getEntityTypeNameForResourceName and MetadataStore.setEntityTypeForResourceName methods. When using Entity framework metadata, breeze assumes that Entity Framework 'EntitySet' names correspond to 'resourceNames'. This assumption is what is causing the problem. If you query a 'resourceName' that is different from one of your EntitySet names, Breeze does not know the corresponding 'entity Type' and falls back on inferring the datatypes used in the query, instead of using the 'entity Type' metadata.

There are two fairly simple fixes, either of which work.

1) Use the MetadataStore.setEntityTypeForResourceName method to map your resource names to your entity types. Note that you can have as many resource names as you like mapped to the same entity type.

2) Name your methods ( that correspond to resource names) to the names of the corresponding EntitySet defined in EF and not the name of the type that is returned. A common convention is that EntitySet names ( and hence resource names) are pluralized, whereas EntityType names are usually singular. i.e. 'Customers' is a resource name and 'Customer' is the EntityType

Upvotes: 1

Related Questions