I am trying to save data in 10 entity tables at once. For this reason I am loading the data in the related entity objects and saving it in the end. I am trying to check if the record exist in the database by
var bankContacts = entity.BankContacts.Where(bc => bc.Bank.Equals(bankObj)).FirstOrDefault();
But I am getting the following error: 'Only primitive types or enumeration types are supported in this context.'
I don't have the value of the Bank.BankID to be able to use
var bankContacts = entity.BankContacts.Where(bc => bc.BankID == bankObj.BankID).FirstOrDefault();
How can I solve this issue?
The current bank object is:
This line ...
var bankContacts = entity.BankContacts.Where(bc => bc.Bank.Equals(bankObj)).FirstOrDefault();
... is comparing the two bank objects with .Equals and EF doesn't know how to convert that to a query expression. Remember EF is not going to run your methods like stand linq but will convert them to SQL queries for operation. You need to compare them in another way such as bc.Bank.Id == etc. If you need to make sure all values are equal then you can write custom comparer for EF or just add multiple .where clauses or multiple || operators to test them all.
'Only primitive types or enumeration types are supported in this context'
it means that only built-in data types or enums types which is converted to int are supported.
EF deosn't know how to translate your query to SQL statements to run it against your database.
Related
I am working with a .Net 6 Console application where I need to read data from tables in a custom DbContext using Microsoft.EntityFrameworkCore
I have added the entities to the model in OnModelCreating() and can get them back using a call to
var entity = ctx.Model.GetEntityTypes().FirstOrDefault(e => e.FullName().InfexOf(tableName) >= 0);
Given that, how to I retrieve a list of data, for example entity.ToList() - the type returned for entity is IEntityType?.
As an alternate (and my preferred way if possible), I have created an array of tables using reflection (they all inherit from BaseTable), they are stored as a list.
I would like to create a DbSet<> using DbContext.Set() so that I can use Find(), AsNoTracking() and other such commands (including write operations).
I have the following:-
IQueryable<Object>dbSet = (IQueryable<Object>)ctx
.GetType()
.GetMethod("Set",1,Type.EmptyTypes)
.MakeGenericMethod(t)
.Invoke(ctx, null);
Which allows me to do something like dbSet.ToList(), but I would really like to cast it to a DbSet.
Does anyone know if it is possible to make such a conversion?
(I am reading only a few records from sets of tables and then writing data back to a different database (with the same tables).
Update: *Another way of thinking about this: I am iterating across a collection of tables. I need to pull out the PK and two other columns (for which I have the name of at runtime) - if the value of column 1 contains a specific value, I need to update the value of column 2 *
You can't really do that. However, you can cast the database value to specific types based on discriminators. Take a look at this https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations
In one of my DB models I have a property with a custom type (Dictionary<string, string>), which is automatically converted to/from JSON with a custom converter, and is stored as a text field in the database. I would like to be able to use MySQL's LIKE comparer to search for a string within this JSON field, but I am getting an exception. I don't care about the JSON's structure, I'm fine with treating it as a simple text field and searching in it that way.
Here's how I try to do it (Sku is the complex type):
var responseSet = database.Products.Where(p => EF.Functions.Like(p.Sku, "%query%"));
And this is the exception I get:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The LINQ expression 'DbSet<ProductObject>()
.Where(p => __Functions_0
.Like(
matchExpression: p.Sku, pattern: __Format_1))' could not be translated.
Additional information:
Translation of method 'Microsoft.EntityFrameworkCore.MySqlDbFunctionsExtensions.Like'
failed. If this method can be mapped to your custom function,
see https://go.microsoft.com/fwlink/?linkid=2132413 for more information.
The link in the exception points to a long git issue with tons of cross-references, but I haven't been able to find anything useful in it.
Is there a way I could prevent this error from happening and search within the complex field?
The fundamental problem with the EF Core value converters is that the LINQ query is build against client type, which then is translated behind the scenes to provider type, and there is no standard way to specify provider type conversion inside the query.
However there is a simple trick with casting (presented in some my answers to other conversion related issues like How can a JSON_VALUE be converted to a DateTime with EF Core 2.2?, Expression tree to SQL with EF Core or Comparing strings as dates using EF core 3), which works for most of the EF Core relational database providers.
In this case, you know that the CLR equivalent of the provider type is string, so you can use the following cast
p => EF.Functions.Like((string)(object)p.Sku, "%query%")
The intermediate cast to object is needed to fool C# compiler to accept the actual cast. EF Core translator is smart enough to remove it (as well as others when not needed like here).
This isn't a supported feature yet, but there is a hacky workaround that might work for you. Define a custom database function to explicitly cast the expression to the underlying db type.
public static string Cast(YourComplexType t) => throw new NotSupportedException();
modelBuilder.HasDbFunction(() => Cast(default))
.HasTranslation(args =>
{
var a = args.First();
return new SqlUnaryExpression(
ExpressionType.Convert,
a,
typeof(string),
new StringTypeMapping(a.TypeMapping.StoreType, DbType.String));
});
EF.Functions.Like(Cast(p.Sku), "%query%")
Unfortunately you can't define generic db functions. The arguments all have to be types the provider can store, which rules out object or an interface. So you may have to define overloaded methods for every possible type.
I have two rateable objects E and S, S is contained within E so you say E.S and they both use the same object R to be rated; R will always have ONLY ONE rated object at a time so if R.E.HasValue == true then R.S.HasValue will never be true.
I have all the R objects from the database and I have E. What would be the lambda expression to get all Rs Where R.S in E.S??
UPDATE
I found this MSDN Documentation where it says:
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType. Only primitive types
('such as Int32, String, and Guid')
are supported in this context."
So... I can't use A-Dubb's answer since the query reads
R.Where(r => r.SID.HasValue).Where(r => E.S.Contains(r.S))
Which results in a NotSupportedException... I still have the property R.SID which is of type int? but how could I then query all E.S to get their IDs?
P.S. E.S. is of type EntityCollection.
I will say that IN clauses are normally represented with call to Contains. For example
public class R {
public int Id {get; set;}
}
public IEnumerable<R> InClause(IEnumerable<int> ids) {
var subset = ids.ToList();
return dbContext.Query<R>.Where(r => subset.Contains(r.Id));
}
I know that's not the exact API call in EF, but it should help set the basis for your solution. Maybe you already know how to generate IN clauses but I figure if you did then you presumably wouldn't be stuck. If the IN clause isn't the difficult part of the query then what is? Maybe the piece of business logic you mentioned? Again, a C# object model would help.
The problem with using contains though, is that its closer to sql "like" than "in". So if you have user entry on say 8 digit ID's and someone
enters 0 then contains will bring you back ALL id's with zero somewhere in it.
[Note After Answer: I am actually querying in memory-objects and that's why ToTraceString doesn't work. I added this to save the reader potential time from reading my long post].
I'm using a ToTraceString command when trying to inspect how my LINQ queries end up looking. However, today my query got a bit complicated, involving a join and all of the sudden, I get this error when I try to Trace my String:
Unable to cast object of type 'd__7a`1[EGSLanguageProviderShared.DTODataType]' to type 'System.Data.Objects.ObjectQuery'.
My Query, and subsequent invocation of ToTraceString is as follows (note that System.Data.Entity has to be referenced in order for this to work). Both objects I'm querying (langDTs and langInstructionsAVDTs) are Entity Framework (.Net 3.5) objects from the same database. My Where Clause (== av.InstructionAVKey) uses a simple Value Collection Class, nothing to see there.
IEnumerable<DTODataType> dts =
(from langDT in langDTs
join langIAVDT in langInstructionsAVDTs
on langDT.DataTypeKey equals langIAVDT.DataTypeKey
where langIAVDT.InstructionAVKey == av.InstructionAVKey
select langDT).Distinct();
var sql = ((System.Data.Objects.ObjectQuery)dts).ToTraceString();
Any ideas on how I could see the LINQ translation of this Join? ::- ). I noticed that System.Data.Objects has more types of queries, but I can't get any of the ones which seem more relevant to this case, to work.
LATER EDIT:
As you recommended, I tried changing the IEnumerable to an IQueryable but that resulted in a type incompatibility compilation error ::- /.
After doing an explicit cast, I got the same error, but at Runtime (Unable to cast object of type '<DistinctIterator>d__7a1[EGSLanguageProviderShared.DTODataType]' to type 'System.Linq.IQueryable1[EGSLanguageProviderShared.DTODataType]'.`)
Additional code: my objects langDTs and langInstructionsAVDTs are:
List<DTOInstructionActiveValueDataType> langInstructionsAVDTs = CurrentLPInstructionManager.GetInstructionsActiveValuesDataTypes((from avKey in langInstructionsAVs select avKey.InstructionAVKey).Distinct().ToArray());
List<DTODataType> langDTs = _LPDataTypeManager.GetDataTypes((from dt in langInstructionsAVDTs orderby dt.DataTypeKey select dt.DataTypeKey).Distinct().ToArray());
So these objects are indeed queried immediately because they are Lists ::- ). As for DTODataType and DTOInstructionActiveValueDataType, they are simple Value Collection Classes, just public Properties, that's all.
EVEN LATER EDIT
Might be of interest that at their root, the objects I'm using are indeed declared as ObjectQuery back in the deepest layer (Entity Framework):
public global::System.Data.Objects.ObjectQuery<instructions> instructions
However, as I bring the data from over the Data Access Layer, I am converting them to Data Transfer Objects (the DTO-prefixed classes you keep seeing), which are simple Value Collection Classes (a property-by-property map of the Entity Objects which I use in order to keep the Data Model completely separated from the View and also to execute any data post-processing in the Presentation side).
Instead of typing your variable as IEnumerable<DTODataType> try IQueryable<DTODataType>, or even var.
I'm guessing somewhere in there your query is getting executed, and the results being stored as IEnumerable, and therefore no longer able to be cast as ObjectQuery
EDIT
Can you please expand your code snippet to show were both langDTs and langInstructionsAVDTs come from?
Based on your subsequent edit, it's clear that you are simply querying and joining in memory collections. That's why you can't cast to ObjectQuery, and that's also why you can't declare the query to be of type IQueryable<T>.
In other words there's no way to do a dump of the SQL being issued, because no SQL is being issued. Once you converted your data over to your in-memory collection of DTOs, you became disconnected from your database, and all your queries became linq-to-objects queries with no corresponding T-SQL being generated.
I don't know how I can do several union with a distinct.
When I use .Distinct with an IEqualityComparer an exception in threw :
LINQ to Entities does not recognize the method 'System.Linq.IQueryable'
My code is
var union = query.Union(query1).Union(query2);
union = union.Distinct(new EqualityComparerTransaction());
LINQ to Entities does not support the overload of Distinct which takes an IEqualityComparer. When you think about it, it really can't, because LINQ to Entities queries will be converted to SQL, and you cannot convert an interface reference to SQL.
Therefore, you must either:
Use the overload of Distinct which does not take any compare, or
Bring both lists into object space and do the Distinct in LINQ to Objects, like this:
var union = query.Union(query1).Union(query2);
union = union.AsEnumerable().Distinct(new EqualityComparerTransaction());
Naturally, the risk here is that you might bring too many records back from the DB server. You could also use both of these techniques in order to do a portion of the comparison on the server and another portion in object space.
The question answered but I just want to share my experience.
Not sure but I think the error message goes with saying Distinct method not supported with this argument I think.
In fact we just want Linq to SQL , a queryable expression that says if this properties same get one of them.
But when we use a class such as EqualityComparerTransaction it can't be translated to sql normally.
There is an another method GetDistict < T >(string propertyName) But sadly it doesn't work as we expected. This method also goes to DB(what else our source) and get some data and evaluate distinct.
If GetDistinct(string propertyName) extention method were do sql convertion operation It could be. But there is no way.
Sadly the single way of doing that is coding your own distinct extention for LINQ_TO_SQL.I don't think it will be easy! So Enumarating data on the server side seems easiest for now.