I have a weird problem with JPA (EclipseLink 2.3.2) and Enum in a Named Query.
I have a named query:
SELECT f FROM FILES f WHERE (:dataType IS NULL OR f.dataType = :dataType)
which results in
Error compiling the query ... invalid enum equal expression, cannot compare enum value of type [com.example.DataTypeEnum} with a non enum value of type [java.lang.Object]
Now i thought i might do an invalid operation, but if i try
SELECT f FROM FILES f WHERE (:dataType IS NULL)
or
SELECT f FROM FILES f WHERE (f.dataType = :dataType)
or even
SELECT f FROM FILES f WHERE (f.dataType = :dataType OR :dataType IS NULL)
the query compiles and runs fine. (but obviously not with the desired result).
Does anyone know what i'm doing wrong?
edit:
I have my entity annotated as such:
#Enumerated(EnumType.STRING)
public DataTypeEnum getDataType() {
return dataType;
}
That is odd. What version are you using? Did you try 2.4?
Related
I have simplified the following example from my code and hoping there's no obvious compilation errors because of it. Lets say I have the following entities (not what i actually have, please assume I have no EF or schema issues, this is just for example):
public class Company
{
public string GroupProperty {get;set;}
public virtual ICollection<PricingForm> PricingForms {get;set;}
}
public class PricingForm
{
public decimal Cost {get;set;}
}
And I want to query like so:
IQueryable DynamicGrouping<T>(IQueryable<T> query)
{
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
return grouping;
}
I get the following error when calling the groupby/select line:
System.Linq.Dynamic.ParseException: 'Argument list incompatible with lambda expression'
What type is "it" when grouped? I have tried using other expressions that assume it is an IGrouping<string, Company>, or a IQueryable<Company>, same error. I've tried just selecting "Cost" and moving the Sum() aggregate into the selector string (i.e. Sum(#0(it)) as Value) and always seem to get the same error.
I eventually tried something along the lines of:
Expression<Func<IEnumerable<Company>, decimal?>> exp = l => l.SelectMany(c => c.PricingForms).Sum(fr => fr.Cost);
However this one, I get farther but when attempting to iterate through the results I got a different error.
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
So, with this dynamic grouping and injecting my own select expression, what should I assume the datatype of 'it' is? Will this even work?
The type of it is IGrouping<TKey, TElement>, where TKey is dynamic based on the keySelector result type, and TElement is the element type of the input IQueryable. Luckily IGrouping<TKey, TElement> inherits (is a) IEnumerable<TElement>, so as soon as you know the input element type, you can safely base selector on IEnumerable<TElement>.
In other words, the last attempt based on Expression<Func<IEnumerable<Company>, decimal?>> is correct.
The new error you are getting is because #0(it) generates Expression.Invoke call which is not supported by EF. The easiest way to fix that is to use LINQKit Expand method:
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
// This would fix the EF invocation expression error
grouping = grouping.Provider.CreateQuery(grouping.Expression.Expand());
return grouping;
(C# code at end of question)
I have the following inheritance chain:
PreRecord <- Record <- (multiple entity types)
Record declares a property ID As Integer.
PreRecord and Record are not EDM types, and do not correspond to tables in the database.
I have a method that takes a generic parameter constrained to PreRecord and builds an EF query with the generic parameter as the element type. At runtime, in the event that T inherits not just from PreRecord but from Record, I would like add an OrderBy operator on ID:
'Sample 1
Function GetQuery(Of T As PreRecord)(row As T) As IQueryable(Of T)
Dim dcx = New MyDbContext
Dim qry = dcx.Set(Of T).AsQueryable
If TypeOf row Is RecordBase Then
'modify/rewrite the query here
End If
Return qry
End Function
If the parameter constraint were to Record I would have no problem applying query operators that use the ID property. How can I make use of a different (narrowing) generic constraint mid-method and still return an IQueryable(Of T) / IQueryable<T>, where T is still constrained to PreRecord?
I tried this:
'Sample 2
qry = dcx.Set(Of T).Cast(Of Record).OrderBy(Function(x) x.ID).Cast(Of PreRecord)()
which doesn't work:
LINQ to Entities only supports casting EDM primitive or enumeration types.
C# equivalent:
//Sample 1
public IQueryable<T> GetQuery<T>(T row) where T : PreRecord {
var dcx = new MyDbContext();
var qry = dcx.Set<T>.AsQueryable();
if (row is RecordBase) {
//modify/rewrite the query here
}
return qry;
}
and this doesn't work:
//Sample 2
qry = dcx.Set<T>.Cast<Record>.OrderBy(x => x.ID).Cast<PreRecord>()
The problem here is the fact that compiler checks queries already at compile time and PreRecord class does not have ID property. We cannot use simply Cast, because when it is used in definition of the query parser tries to convert it to sql - but there is no such thing that exists in sql. Sql supports only conversion of one column type to another - so on the .NET side it is supported only for primitive and enum types. To overcome compiler query checking we may use Expression class to build dynamic queries:
ParameterExpression e = Expression.Parameter(typeof(Record));
Expression body = Expression.Property(e, "ID");
Expression<Func<PreRecord, int>> orderByExpression = Expression.Lambda<Func<PreRecord, int>>(body, e);
And use your expression in the query:
qry = dcx.Set<T>.OrderBy(orderByExpression);
This way your linq query will not be validated during compile time but execution time. Here I assumed ID is of type int, if the type is different change it accordingly.
I'm trying to make a type extension for MongoDB C# Driver that will return an option type instead of null when trying to execute a query that yields 0 results.
I've run into a few issues on the way, but right now there is just one thing in the way.
Here is the code
[<Extension>]
type Utils () =
[<Extension>]
static member inline tryFindOne(x: MongoCollection<'T>, query) =
match x.FindOne(query) with
| null -> None
| value -> Some value
[<CLIMutable>]
type Entity =
{ Id : ObjectId; Name : string }
static member Create(name) =
{ Id = ObjectId(); Name = name }
The problem of course is that the record type Entity to the F# compiler does not conform to the type constraint of the extension method('T : null) but I have to have the contraint to be able to pattern match against nulls. Of course it's sort of a nonsensical thing because the type Entity is very much "nullable" for interop purposes and will be returned as null whenever you try to query a MongoDB collection which yields 0 results. I tried to set the attribute [<AllowNullLiteral>] but unfortunately it only works with classes. So alas I'm stuck, I could make Entity into a class instead but I think records are more idiomatic F#.
I think the following should work:
[<Extension>]
type Utils () =
[<Extension>]
static member inline tryFindOne(x: MongoCollection<'T>, query) =
let theOne = x.FindOne(query);
if (box theOne = null) None else Some(theOne)
I have borrowed the idea from Sergey Tihon's post here:
https://sergeytihon.wordpress.com/2013/04/10/f-null-trick/
Alternative way to do the null check without boxing:
[<Extension>]
type Utils () =
[<Extension>]
static member inline tryFindOne(x: MongoCollection<Entity>, query) =
let theOne = x.FindOne(query)
if obj.ReferenceEquals(theOne, null) then None else Some(theOne)
I am using jpa 2 with eclipslink...
I am trying to execute this query:
List postEntityList = entityManager.createQuery("select p from PostEntity p where p.organizationEntity=?1 and p.organizationUnitEntity=?2 and p.organizationLocationEntity=?3 and p.organizationBatchEntity=?4 and p.postCategoryEntity=?5 and p.dateOfPosting between ?6 and ?7 order by p.rating desc").setParameter(1, organizationEntity).setParameter(2, organizationUnitEntity).setParameter(3, organizationLocationEntity).setParameter(4, organizationBatchEntity).setParameter(5, postCategoryEntity).setParameter(6, minimumTime).setParameter(7, maximumTime).getResultList();
But I am getting the error:
You have attempted to set a value of type class entity.PostCategoryEntity for parameter 5 with expected type of class entity.OrganizationEntity from query string select p from PostEntity p where p.organizationEntity=?1 and p.organizationUnitEntity=?2 and p.organizationLocationEntity=?3 and p.organizationBatchEntity=?4 and p.postCategoryEntity=?5 and p.dateOfPosting between ?6 and ?7 order by p.rating desc.
Why is it so!!! Any idea...??
The class of parameter5 should be entity.
Maybe your parameter5 is a FK, if it is, you need to use parameter5.parameter to find the real variable.
I have a situation where I'm trying to filter a LINQ select using a derived sub class.
ctx.BaseEntity.OfType<SubClass>() - this works fine.
However I'd like to do this using a string value instead. I've come across a performance barrier when I have lots (>20) Sub Classes and selecting an Entity without using OfType just isn't an option. I have a generic UI that renders from the base class, so I don't know what Class Type will be returned at compile time.
So what I'd like to do is this:
Perform a projected Select where I
return just the SubClassType from
the database
Perform a second select
using this value as the OfType to
only select the relevant related
entity from the database (No mass
unions generated)
int id = 1;
var classType = (from c in ctx.BaseClass.Include("ClassType")
where c.id == id
select new
{
c.ClassType.TypeName
}).First();
BaseClass caseQuery = ctx.BaseClass.OfType<classType.TypeName>()
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id);
But obviously this won't work because OfType requires a Type and not a string.
Any ideas on how I can achieve this?
Update:
As a side note to the original question, it turns out that the moment you project a query that uses a Navigation Property - it builds the monster SQL too, so I've ended up using a stored procedure to populate my ClassType entity from the BaseClass Id.
So I've just got it to work using eSQL, which I'd never used before. I've posted the code here just in case it helps someone. Has anyone else got a more strongly typed solution they can think of?
BaseClass caseQuery = ctx.BaseClass.CreateQuery<BaseClass>("SELECT VALUE c FROM OFTYPE(Entities.[BaseClass],namespace.[" + classType.TypeName + "]) as c")
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id).FirstOrDefault();
To answer the headline question about calling OfType with a string / runtime type, you can do the following:
// Get the type, assuming the derived type is defined in the same assembly
// as the base class and you have the type name as a string
var typeToFilter = typeof(BaseClass)
.Assembly
.GetType("Namespace." + derivedTypeName);
// The use reflection to get the OfType method and call it directly
MethodInfo ofType = typeof(Queryable).GetMethod("OfType");
MethodInfo ofTypeGeneric = method.MakeGenericMethod(new Type[] { typeToFilter });
var result = (IQueryable<Equipment>)generic.Invoke(null, new object[] { equipment });
Combine this with your stored procedure to get the class name and you (should?) avoid the massive join - I don't have table-per-type implementation to play with so I can't test.