Entity Framework Core PostgreSQL EF.Functions.JsonTypeof - postgresql

.Where(e => EF.Functions.JsonTypeof(e.Customer.GetProperty("Age")))
This syntax in https://www.npgsql.org/efcore/mapping/json.html?tabs=data-annotations%2Cjsondocument not works!! show this error: cannot implicitly convert "string" to "bool"

The sample syntax from the link is simply incomplete. JsonTypeof returns string an according to the link is mapped to jsonb_typeof which
Returns the type of the outermost JSON value as a text string. Possible types are object, array, string, number, boolean, and null.
So the correct sample usage in Where clause would compare the return value of the method to a string containing one of the aforementioned values, for instance
.Where(e => EF.Functions.JsonTypeof(e.Customer.GetProperty("Age")) == "number")

Related

Convert results of EF query to string array

I have an EF query that returns integers. I'm trying to build the selected values list for a MultiSelectList to build a select dropdown with MVC. It has to be a string array (not a list). I have the following:
string[] SelectedValues = context.ContactContactTypes.Where(x => x.ContactID == ID).Select(x => new { ID = x.ID.ToString() }).ToArray();
but it throws an error:
Cannot implicitly convert type 'anonymous type: string ID[]' to 'string[]'
I can return a list and use a foreach to build the array using .NET to convert each value. Is there a more elegant way of doing this within the EF query itself?

How to query a JSON element

Let's say I have a Postgres database (9.3) and there is a table called Resources. In the Resources table I have the fields id which is an int and data which is a JSON type.
Let's say I have the following records in said table.
1, {'firstname':'Dave', 'lastname':'Gallant'}
2, {'firstname':'John', 'lastname':'Doe'}
What I want to do is write a query that would return all the records in which the data column has a json element with the lastname equal to "Doe"
I tried to write something like this:
records = db_session.query(Resource).filter(Resources.data->>'lastname' == "Doe").all()
Pycharm however is giving me a compile error on the "->>"
Does anyone know how I would write the filter clause to do what I need?
Try using astext
records = db_session.query(Resource).filter(
Resources.data["lastname"].astext == "Doe"
).all()
Please note that the column MUST have a type of a JSONB. The regular JSON column will not work.
Also you could explicitly cast string to JSON (see Postgres JSON type doc).
from sqlalchemy.dialects.postgres import JSON
from sqlalchemy.sql.expression import cast
db_session.query(Resource).filter(
Resources.data["lastname"] == cast("Doe", JSON)
).all()
If you are using JSON type (not JSONB) the following worked for me:
Note the '"object"'
query = db.session.query(ProductSchema).filter(
cast(ProductSchema.ProductJSON["type"], db.String) != '"object"'
)
I have some GeoJSON in a JSON (not JSONB) type column and none of the existing solutions worked, but as it turns out, in version 1.3.11 some new data casters were added, so now you can:
records = db_session.query(Resource).filter(Resources.data["lastname"].as_string() == "Doe").all()
Reference: https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.JSON
Casting JSON Elements to Other Types
Index operations, i.e. those invoked by calling upon the expression
using the Python bracket operator as in some_column['some key'],
return an expression object whose type defaults to JSON by default, so
that further JSON-oriented instructions may be called upon the result
type. However, it is likely more common that an index operation is
expected to return a specific scalar element, such as a string or
integer. In order to provide access to these elements in a
backend-agnostic way, a series of data casters are provided:
Comparator.as_string() - return the element as a string
Comparator.as_boolean() - return the element as a boolean
Comparator.as_float() - return the element as a float
Comparator.as_integer() - return the element as an integer
These data casters are implemented by supporting dialects in order to
assure that comparisons to the above types will work as expected, such
as:
# integer comparison
data_table.c.data["some_integer_key"].as_integer() == 5
# boolean comparison
data_table.c.data["some_boolean"].as_boolean() == True
According sqlalchemy.types.JSON, you can do it like this
from sqlalchemy import JSON
from sqlalchemy import cast
records = db_session.query(Resource).filter(Resources.data["lastname"] == cast("Doe", JSON)).all()
According to this, pre version 1.3.11, the most robust way should be like this, as it works for multiple database types, e.g. SQLite, MySQL, Postgres:
from sqlalchemy import cast, JSON, type_coerce, String
db_session.query(Resource).filter(
cast(Resources.data["lastname"], String) == type_coerce("Doe", JSON)
).all()
From version 1.3.11 onward, type-specific casters is the new and neater way to handle this:
db_session.query(Resource).filter(
Resources.data["lastname"].as_string() == "Doe"
).all()

Possible bug in breeze 1.4.14

I haven't tested this against the 1.4.16 release that came out a couple of weeks ago but there is nothing in the release notes about it.
The problem occurs with predicates where the value you are comparing is identical to the name of a property on any entity that breeze knows about. A simple test case is :
var query = breeze.EntityQuery.from('Items');
var pred = breeze.Predicate.create('name', breeze.FilterQueryOp.Contains, searchTerm);
query = query.where(pred);
Where searchTerm is equal to any string other than "name" this produces an oData query as below:
Items?$filter=(substringof(%27somevalue%27%2CName)%20eq%20true)
but if searchTerm = "name" then it produces the following query
Items?$filter=(substringof(Name%2CName)%20eq%20true)
Which istead of comparing the string 'name' against the property Name, it compares the property Name with itself.
I have not tested every operator but as far as I can tell it does not matter which you use you get the same behaviour.
You also get the same problem when querying navigation properties but it usually results in an invalid query. Below is a predicate for the same entity but against a navigation property tags that contains a collection of ItemTag entities that have a "Tag" property on them.
breeze.Predicate.create('tags', breeze.filterQueryOp.Any, 'tag', breeze.filterQueryOp.Contains, searchTerm)
It works fine for any searchTerm other than "tag" where it produces an oData request as below:
Items?$filter=Tags%2Fany(x1%3A%20substringof(%27somevalue%27%2Cx1%2FTag)%20eq%20true)
but if the searchTerm is "tag" then it requests:
Items?$filter=Tags%2Fany(x1%3A%20substringof(Tag%2Cx1%2FTag)%20eq%20true)
which produces an error of "Could not find a property named 'Tag' on type 'Item'" because the property Tag exists on the ItemTag entity.
In short breeze seems to infer that any search term that is identical to the name of a property it knows about, refers to that property rather than being a string literal value.
Has anyone else encountered this?
Is this a bug, or is there a way to explicitly tell breeze to interpret that value as a string literal and not a reference to a property?
I am not sure it is relevant as the server seems to be responding correctly to the requests and it is breeze that is creating incorrect requests but on the server side I am using Web API oData controllers with EF as ORM data layer.
Try
var pred = breeze.Predicate.create('name', breeze.FilterQueryOp.Contains,
{ value: searchTerm, isLiteral: true} );
This is described here ( under the explanation of the value parameter):
http://www.breezejs.com/sites/all/apidocs/classes/Predicate.html#method_create
if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value' property and an 'isLiteral' property set to either true or false.
Breeze also tries to infer the dataType of any literal based on context, if this fails you can force this inference by making the value argument an object with a 'value' property and a 'dataType'property set
to one of the breeze.DataType enumeration instances.
The reason for this logic is to allow expressions where both sides of the expression are properties. For example to query for employees with the same first and last name you'd do this:
var q = EntityQuery.from("Employees")
.where("lastName", "==", "firstName");
whereas if you wanted employees with a lastName of 'firstName' you'd do this:
var q = EntityQuery.from("Employees")
.where("lastName", "startsWith", { value: "firstName", isLiteral: true })

Using max() function with entity framework throws an exception

So I need to get the max value of a column (or the last one) using entity framework and both of these queries throw exceptions:
(The ID I'm trying to retrieve is of type varchar, but it works in raw sql, I think
it should work here too)
This one:
string maxCurrentID = db.reservations.Max().ReservationID;
Throws this:
The specified method 'EntityClub_.reservacion Maxreservacion' on the type 'System.Linq.Queryable' cannot be translated into a LINQ to Entities store expression because no overload matches the passed arguments.
and this one:
string maxCurrentID = db.reservations.LastOrDefault().ReservationID;
LINQ to Entities does not recognize the method 'EntityClub_.reservacion LastOrDefaultreservacion' method, and this method cannot be translated into a store expression.
How can I obtain the expected values?
var maxReservationId=db.reservations.Max(u =>(int?)u.ReservationID) ?? 0;
if there is no data in table it replaces null with 0
You aren't asking for the highest ReservationID, you're trying to get the highest Reservation, and getting its ReservationID. EF does not understand what "the highest Reservation" means.
var maxReservationID = db.reservations.Max(r => r.ReservationID);
or
var maxReservationID = db.reservations.Select(r => r.ReservationID).Max();
should work.

EntLib Way to Bind "Null" Value to Parameter

I wish to pass Null Value to the parameter as follow:
_db.AddInParameter(dbCommand, "Id", DBNull.Value, myContactPerson.Id);
I am receiving the following error :
"can not convert "System.DBNull to System.Data.DbType".
I know the meaning of this error.
But i need to supply null value to myContactPerson.Id
How can i achieve this ?
If myContactPerson.Id isn't an auto-number, then why not just pass 0.
DBType should be passed in that parameter and should match your the dbtype (string, int, etc.) for the table that you are comparing with in your database. You would replace your value field "myContactPerson.Id" with DBNull.Value to always pass the null value.