Convert results of EF query to string array - entity-framework

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?

Related

Entity Framework, Linq : concatenating results from child table

I have an existing linq query which gets some data into a view model object. This is working fine.
I want to add a new property for data from child table which will have column values from a child table in a comma separated string format.
Problem: I am not able to concatenate the results using string.join
Simplified version of tables showing only relevant fields
part
id
part number
1
ABC1
2
DEF1
vendor
id
vendorname
1
acme
2
john
vendor part name (vendor specific part number)
partid
vendorid
partname
1
1
GDSE-553-32
1
2
JWWVV-HH-01
simplified version of query
result = (from p in DBContext.Parts.Where(w => w.EquipmentId == eId)
select new PartModel
{
Id = p.Id,
Number = p.PartNumber,
VendorPartNames= String.Join(",", DBContext.VendorPartName.Where(w => w.PartId == p.Id).Select(s => s.PartName))//this line causes exception (shown below)
});
Exception:
LINQ to Entities does not recognize the method 'System.String Join(System.String, System.String[])' method, and this method cannot be translated into a store expression.
Please note: the actual query has some joins and other columns, so please dont suggest solutions that requires joins.
If I change the "VendorPartName" to a List type , I can get the results without any problems.
My only problem is in "How to convert the results for "VendorPartName" property to a comma separated strings?"
eg: based on sample table data provided, it should be
GDSE-553-32, JWWVV-HH-01
Entity Framework does not support String.Join() method.
So, what we can do is to fetch VendorPartNames as a string collection and then we can later separate it with ,.
Note: For this, we would first use an anonymous object and later convert it to PartModel.
So your query would look like this:
var parts = DBContext.Parts
.Where(w => w.EquipmentId == eId)
.Select(p => new {
Id = p.Id,
Number = p.PartNumber,
VendorPartNames = p.VendorPartName.Select(n => n.PartName)
}).ToList();
var result = parts.Select(i => new PartModel {
Id = i.Id,
Number = i.Number,
VendorPartNames = String.Join(",", i.VendorPartNames)
}).ToList();

Call jsonb_contains function (postgres) using JPA criteria and JsonBinaryType

I have a JPA/Hibernate entity which has a JSONB column (using https://github.com/vladmihalcea/hibernate-types ) for storing a list of strings. This works fine so far.
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
#Type(type = "jsonb")
#Column(name = "TAGS", columnDefinition = "jsonb")
private List<String> tags;
Now I want to check if another string is contained in the list of strings.
I can do this by writing a native query and use the #> operator from Postgres. Because of other reasons (the query is more complex) I do not want to go in that direction. My current approach is calling the jsonb_contains method in a Spring Data specification (since the operator is just alias to this function), e.g. jsonb_contains('["tag1", "tag2", "tag3"]','["tag1"]'). What I am struggling with is, getting the second parameter right.
My initial approach is to also use a List of Strings.
public static Specification<MyEntity> hasTag(String tag) {
return (root, query, cb) -> {
if (StringUtils.isEmpty(tag)) {
return criteriaBuilder.conjunction();
}
Expression<Boolean> expression = criteriaBuilder.function("jsonb_contains",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(List.of(tag)));
return criteriaBuilder.isTrue(expression);
};
}
This results in the following error.
Caused by: org.postgresql.util.PSQLException: ERROR: function jsonb_contains(jsonb, character varying) does not exist
Hinweis: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 375
It does know that root.get("tags") is mapped to JSONB but for the second parameter it does not. How can I get this right? Is this actually possible?
jsonb_contains(jsob, jsonb) parameters must be jsonb type.
You can not pass a Java String as a parameter to the function.
You can not do casting in Postgresql via JPA Criteria.
Using JSONObject or whatever does not help because Postgresql sees it as
bytea type.
There are 2 possible solutions:
Solution 1
Create jsonb with jsonb_build_object(text[]) function and send it to jsonb_contains(jsonb, jsonb) function:
public static Specification<MyEntity> hasTag(String tag) {
// get List of key-value: [key1, value1, key2, value2...]
List<Object> tags = List.of(tag);
// create jsonb from array list
Expression<?> jsonb = criteriaBuilder.function(
"jsonb_build_object",
Object.class,
cb.literal(tags)
);
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains",
Boolean.class,
root.get("tags"),
jsonb
);
return criteriaBuilder.isTrue(expression);
}
Solution 2
Create custom function in your Postgresql and use it in Java:
SQL:
CREATE FUNCTION jsonb_contains_as_text(a jsonb, b text)
RETURNS BOOLEAN AS $$
SELECT CASE
WHEN a #> b::jsonb THEN TRUE
ELSE FALSE
END;$$
LANGUAGE SQL IMMUTABLE STRICT;
Java Code:
public static Specification<MyEntity> hasTag(String tag) {
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains_as_text",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(tag)
);
return criteriaBuilder.isTrue(expression);
}
I think that the reason is that you pass the varchar as the second param. jsonb_contains() requires two jsonb params.
To check a jsonb array contains all/any values from a string array you need to use another operators: ?& or ?|.
The methods bindings for them in PSQL 9.4 are: jsonb_exists_all and jsonb_exists_any correspondingly.
In your PSQL version, you could check it by the following command:
select * from pg_operator where oprname = '?&'

Entity Framework Core PostgreSQL EF.Functions.JsonTypeof

.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")

Not able to use IN query in LINQ with Entity Framework

I am using EF Framework to retrieve the data from SQL DB.
Sub Request Table looks like below:
In this table "org_assigneddept" is foreign key to another Department Table.
I have list of Departments as Input and I want to retrieve only those rows from DB whose org_assigneddept is matching the list.
Please find my whole code:-
private List<EventRequestDetailsViewModel> GetSummaryAssignedDeptEventRequests(List<EmpRoleDeptViewModel> vmDept)
{
List<EventRequestDetailsViewModel> vmEventRequestDeptSummary = new List<EventRequestDetailsViewModel>();
RequestBLL getRequestBLL = new RequestBLL();
Guid subRequestStatusId = getRequestBLL.GetRequestStatusId("Open");
using (var ctxGetEventRequestSumm = new STREAM_EMPLOYEEDBEntities())
{
vmEventRequestDeptSummary = (from ers in ctxGetEventRequestSumm.SubRequests
where vmDept.Any(dep=>dep.DeptId == ers.org_assigneddept)
select new EventRequestDetailsViewModel
{
SubRequestId = ers.org_subreqid
}).ToList();
}
}
It is giving the following error at the LINQ Query level:-
System.NotSupportedException: 'Unable to create a constant value of
type 'Application.Business.DLL.EmpRoleDeptViewModel'. Only primitive
types or enumeration types are supported in this context.'
Please let me know as how can I achieve the result
You cannot pass the department VMs to SQL, it doesn't know what those are.
// Extract the IDs from the view models.. Now a list of primitive types..
var departmentIds = vmDept.Select(x => x.DeptId).ToList();
then in your select statement...
..
where departmentIds.Contains(id=> id == ers.org_assigneddept)
..

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.