DocumentDB LINQ query not converting enums correctly - linq

According to this and this, The DocumentDb LINQ provider is supposed to use custom JsonConverters when generating queries that use enums (as far back as version 1.10.0). But we're not seeing that behavior.
My project is referencing Microsoft.Azure.DocumentDb 1.13.1 and we're still seeing a LINQ query that converts an enum to its numeric value. A Where() predicate like this
request => request.Source == sourceId && request.State == state
generates a query like this
{SELECT * FROM root WHERE ((root["Source"] = "5c196602-1a60-406a-81cd-1be5ac23eb18") AND (root["State"] = 0))) }
State is an enum and is correctly serialized/stored in the DB as its string value, per our serializer settings on the docdb client object. This is not a problem with serializing/deserializing the objects to/from documents - that works as expected.
What might we be doing wrong? Is it documented somewhere how to enable/utilize this capability? Do we have to register the JSON converters with the LINQ provider or something?

1.13.1 is a version with query serialisation issues so it is a bug with the SDK.
Those issues where fixed after 1.19.1 as you can see here: https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sdk-dotnet#a-name11911191
Fixed a bug in which the custom JsonSerializer settings were not being honored for some queries and stored procedure execution
You will have to upgrade to a post 1.19.1 version of the SDK.

Related

Getting "object is not extensible" when trying to add fields to result gql data objects after upgrading to Apollo client 2

When getting results from the server, usually I'm adding extra fields to the object in my Apollo Angular client, for convenience usage later on, like this:
this.apollo.watchQuery<any>({query: gql`...`})
.valueChanges.pipe(
map(result => {
result.data.sites.forEach(s => {
s.fullName = `${s.parent.displayName} - {s.displayName}`;
}
return result;
});
This used to work fine with Apollo client 1. Now I'm upgrading my code to Apollo client 2 and the latest apollo-cache-inmemory and I'm getting an error when trying to do the above:
TypeError: Cannot add property fullName, object is not extensible
I realize I can make deep copies of the objects and that would resolve, but why is this change? Is there a way to make Apollo return an extensible object like before? I have many usages of queries in my code and in almost all of them I'm adding fields to the result like the above, so it'll be a fairly big code change. Also, deep copy will have a slight performance issue.
Thanks!
Do not mutate the result, just update the fullName in immutable way.

Query by Enum in RavenDB

I have used the convention:
store.Conventions.SaveEnumsAsIntegers = true;
Enums are now being persisted as integers correctly, but, when i try to query using an enum the query gets translated with the enums in their string representation, which gives me no results.
session.Query<Entity>().Where(x => x.EnumProp == MyEnum.Value1);
It was my impression that SaveEnumsAsIntegers converts both when persisted and when querying as per this post:
Querying an enum property persisted as an integer is not working
Can anyone help ?
I have tested this against RavenDB 2330 and it is working as expected.
See the passing unit test here.
If there's something you are doing differently, please update your question. Thanks.

Linq IQueryable.Any() Usage

Below is my code . Please review it .
1. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId == Guid.Parse(host.ID) &&
p.Status == (int)DeployStatus.Deploying).AsEnumerable().Any();
This one works.
The following statements doesn't work.
2. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId == Guid.Parse(host.ID) &&
p.Status == (int)DeployStatus.Deploying).Any();//Error
The Exception is
An exception of type 'System.NotSupportedException' occurred in
Microsoft.Data.Services.Client.DLL but was not handled in user code
Additional information: The method 'Any' is not supported.
3. bool isUnavailable = db.Deploys.Where(p =>
p.HostEnvironmentId.ToString() == host.ID &&
p.Status == (int)DeployStatus.Deploying).AsEnumerable().Any();//Error
The Exception is
An exception of type 'System.NotSupportedException' occurred in
Microsoft.Data.Services.Client.DLL but was not handled in user code
Additional information: The expression (([10007].HostEnvironmentId.ToString() ==
"b7db845b-cec4-49af-8f4b-b419a4e44331") And ([10007].Status == 90)) is not supported.
The Deploys Class is the model which is built in the client proxy class of WCF Data service. I was using "add service reference" to create WCF client proxy class.
But as to the Generic List,
Supposed below code. it will works fine.
4.bool b=servers.Where(d =>
d.status == (int)Enums.ServerStatus.Deploying ||
d.status == int)Enums.ServerStatus.Unavailable).Any();
My question is
Why same way used in different Class got different result .(See the method 2 and method 4).
Why 2 and 3 don't work.
Hope someone can help me . Thanks
LINQ has a concept of 'providers'. When working with LINQ over different data sources, different things need to happen for identical LINQ queries depending on the data source.
For example, when you want to use LINQ to query a database, the LINQ query needs to be converted to an SQL query. When the data source is OData, the query needs to be converted to a URL. There are different providers for each, and each provider supports a different subset of LINQ operators and other language constructs. LINQ-to-SQL, Entity Framework and LINQ-to-NHibernate are three popular LINQ providers for database access.
In your case, you are using WCF Data Services which includes a LINQ provider for OData. Since in OData there is no way to express the .Any() LINQ operator, attempting to use it in a query with that provider throws an exception. By using .AsEnumerable() you're essentially saying to stop using the OData LINQ provider at that point and start using the LINQ-to-Objects provider (which isn't technically a provider, but conceptually you can think of it as one). That means only what comes before .AsEnumerable() will be converted to an OData query, causing to retrieve all the Deploy entities that match the .Where(), and after they have all been transferred to the client, the client will perform the .Any() by checking the number of Deploy entities it has received. This of course is bad if there are many such entities, it will cause unwanted transfer of data over the network when all you wanted is the server side (database probably) to check if there are any. Unfortunately, .Any() is not supported by OData 1.0 (I don't know about OData 2.0).
Also, OData may not support .ToString() either. You may need to compare the Guid structures directly, i.e. create a local variable that contains the GUID value you want to compare:
var g = Guid.Parse("b7db845b-cec4-49af-8f4b-b419a4e44331")`
And then in the query compare the GUIDs like so:
x.HostedEnvironment == g

NHibernate.LINQ Supported Operators

I'm trying to evaluate NHibernate.LINQ 1.0 without actually writing any code. Ayende has admitted that this version of LINQ support is subpar compared to EF, but for the life of me I can't seem to find a page that explains what's supported and unsupported in this implementation. For example, can I use Skip & Take? What can I not use?
You can check LINQ for NHibernate examples to see tests done by Ayende himself on what's implemented and what's not for this provider.
Some of those generally supported:
Anonymous type creation. new { Person = x.Name }
First(). query.First()
FirstOrDefault(). query.FirstOrDefault()
Single(). query.Single()
SingleOrDefault(). query.SingleOrDefault()
Aggregate(). query.Aggregate((x1,x2) => x1)
Contains(). query.Where(x => x.Name.Contains("Foo"))
StartsWith().
EndsWith().
Substring(). where db.Methods.Substring(e.FirstName, 1, 2) == "An"
Sub-queries. query.Where(x => x.Company.Id == 4)
Count(). query.Where(x => x.Relatives.Count > 0)
Any(). query.Any()
Take(). query.Take(10)
Skip(). query.Take(10).Skip(4)
OrderBy(). orderby x.Name descending
Replace(). AfterMethod = e.FirstName.Replace("An", "Zan"),
CharIndex(). where db.Methods.CharIndex(e.FirstName, 'A') == 1
IndexOf(). where e.FirstName.IndexOf("An") == 1
Problematic:
Group by
Joins
One of my own examples:
query = NSession.Session.Linq<Catalog>()
.Where(acc => acc.Company.Status == "A")
.Where(acc => acc.Id.StartsWith("12-536"))
.Where(acc => acc.Id.EndsWith("92") || acc.Id.EndsWith("67"))
.Take(10).OrderBy(acc => acc.Title);
If you're production application is using the latest stable build 2.1.2.4 like I am, you're stuck with what the NHibernate.Linq provider gives us until NHibernate 3.0 (trunk) gets a stable release and we feel safe enough to use it on major applications. Until then, I'm more than happy with a mixture of NHibernate.Linq and HQL.
The basic test of whether NHibernate can work with a Linq statement is whether you can serialize that statement's expression tree, then deserialize it in a different process and get the right answer. That means no external closures; the lambda must work only with what it creates or is given as a parameter.
Linq2NH 1.0, IIRC, also chokes when using members of the class that are not mapped, so if, for instance, you have a read-only calculated property like a special weighted or rolling average, you must map it to a DB column in order to reference it in the lambda (or recreate the logic in the lambda). This is because the expression tree will eventually be boiled down into SQL (through one of NH's intermediates; in 2.x it's ICriteria, in 3.x it's HQL) and if NH cannot take an expression and convert it 1:1 into a SQL expression that will evaluate successfully, it's just not going to work.
There is one special case: Linq2NH, IIRC, is smart enough to turn an IList.Contains() expression into an IN clause. The list must be defined within the lambda (like new[]{"1","2"}.Contains(m.ID)).
The blog post from Ayende is from May this year. A lot of things changed.
The NHiberante. Linq 1.0 linq provider is deprecated since about a year because of the new linq provider in the NHibernate Trunk. The new linq provider is not completely finished yet, but already very complete and usable for much more than the old linq provider. Things that do not work with the new linq provider are considered bugs and will be solved some day when reported.
You can use skip and take with the old and new linq provider. The current list of known issues can be found on NHibernate Jira. Other issues are unknown and all other features are already supported.

OrderBy("it." + sort) -- Hard coding in LINQ to Entity framework?

I have been trying to use dynamic LINQ to Entity in my application for specifying the OrderBy attribute at runtime. However when using the code as described in the majority of documentation:
var query = context.Customer.OrderBy("Name");
I received the following exception:
System.Data.EntitySqlException: 'Name' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly.
After much searching I found this MSDN page:
http://msdn.microsoft.com/en-us/library/bb358828.aspx
Which included the following code example:
ObjectQuery<Product> productQuery2 = productQuery1.OrderBy("it.ProductID");
This prompted me to change my code to the following:
var query = context.Customer.OrderBy("it.Name");
After this the code works perfectly. Would anyone be able to confirm that this is indeed the correct way to get OrderBy working with LINQ to Entity? I can’t believe that the framework would have been implemented in this way, perhaps I have overlooked something?
Thanks, Matt
The it.Name syntax is ESQL and is indeed specific to the EF. There are good reasons to use this sometimes (e.g., collation specifiers), but it's not what I normally do.
Usually I use standard LINQ expressions:
var query = context.Customer.OrderBy(p => p.Name);
You can also use System.Linq.Dynamic, if you download it from Code Gallery, and then your original query:
var query = context.Customer.OrderBy("Name");
...will work.
No nice way, so far
My answer to this question was to create a stored procedure which has parameter to control sorting.

Resources