Why is Entity Framework building non-SARgable predicates - entity-framework

We are using Entity Framework 6.1.1 with code first and DbContext.
Take this a simple query:
Properties.Where(p => p.PropertyID == "aPRoperty")
The generated and executed SQL query is:
WHERE N'aPRoperty' = [Extent1].[PropertyID]
However, propertyID in the database is varchar(10). So the above predicate does an Index Scan rather than Index Seek. This is due to the different datatypes in the predicate.
I can force the correct query by using:
Properties.Where(p => p.PropertyID == DbFunctions.AsNonUnicode("aPRoperty"))
This generates the parameter without the N unicode specifier and we get an Index Seek.
Is this a bug? The fields is mapped IsUnicode(false) in the code first mapping.
Is there a global way to configure this without having to use the Dbfunction in every query we do against a char or varchar field in the database?

You can globally map all strings to varchar in the OnModelCreating event.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(c => c.IsUnicode(false));
}

Sorry for the false alarm all. It seems like it is working correctly after I rebuilt my model dll and reloaded it into linqpad.
My apologies to the EF team for cursing your name for a few minutes there.

Related

How to convert string to DateTime in C# EF Core query

var res = Context.Exampletable
.Where(s => s.CompanyId == CompanyId &&
Convert.ToDateTime(s.TextDate) >= DateTime.Now)
.Select(x => new Exampletable { TextDate = x.TextDate })
.FirstOrDefault();
This is the Linq for one of my problem statements. I want to fetch records future date records from current date & timestamp, so I am converting and comparing it to Datetime but I get this error:
The LINQ expression 'DbSet
.Where(a => Convert.ToDateTime(a.TextDate) > Convert.ToDateTime(DateTime.Now))' > could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
Note: in Postgresql DB TextDate column has string datatype and contains values like '4/1/2020 10:00 AM'.
Please provide a solution for this.
If you really can't change the underlying column type, then instead of unsupported Convert.ToDateTime use C# cast operator which maps to PostgreSQL CAST operator:
(DateTime)(object)s.TextDate >= DateTime.Now
Note that the "intermediate" cast to object is needed just to make the C# compiler happy.
P.S. I really have no idea why some methods of Convert like ToInt32 are supported, and other like ToDateTime are not. I guess just yet another EF Core inconsistency.
It always baffles me that people decide to store DateTime values as strings and then order users of the database to do calculations with the values. I can imagine you'd like to curse the person who decided to do this, especially because he decided to store it in this non-sortable fashion.
Best solution
If possible, change the database such that it stores DateTimes as DateTimes, or if your database language doesn't know how to do that, store the Ticks of the DateTimes as longs. Future users of the database will glorify your name!
long nowTicks = DateTime.Now.Ticks;
var result = Context.Exampletable
.Where(example => example.CompanyId == CompanyId && example.DateTicks >= nowTicks);
Almost best solution
if the decision to save DateTimes as strings is a decision of the developers of PostgreSQL, then try to find if they have functions to handle these datetimes, especially comparison
The "it's getting worse" solution
Try to find out if PostgreSQL has string manipulation functions, so you can translate 4/1/2020 10:00 AM into something IComparable. It is difficult if you want to write code to compare this value with for example 4/1/2019 10:00 AM, or 4/2/2020 10:00 AM, so I guess it will be hell of a job to do this in SQL
With EF Core 5+ you can use ValueConverters as a "workaround" for this scenario and use the built in StringDateTimeConverter (https://apisof.net/catalog/e0dd77d4-73c3-6bba-e51a-4842a59894d1).
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Exampletable>()
.Property(e => e.TextDate)
.HasConversion<string>();
}
public class ExampleTable
{
public DateTime TextDate {get; set;}
}
Then simply
DateTimeOffset someValue = some-value-here;
var query = DbContext.Entities.Where(e => e.TextDate <= someValue);
It is important that the parameter inside your LINQ query is a datetimeoffset for this to work as EF CORE doesn't generate a SQL with CAST operation for the column if you don't. See here for an example https://gist.github.com/dasiths/19b885c58442226d9fc8b89bc78511e4
The generated SQL will be like
((#__startSearch_0 >= CAST([s].[TextDate]) AS datetimeoffset))
Edit: To reiterate, using value converters is a "workaround" here. To see a full analysis of the options, I've written a full detailed analysis of the "work arounds" with the value converter hack here https://dasith.me/2022/01/23/ef-core-datetime-conversion-rabbit-hole/.

Cannot insert explicit value for identity column in table 'LeistungGruppe' when IDENTITY_INSERT is set to OFF

I'm trying to add an EntityObject to my database by calling AddToLeistungGruppe.
LeistungGruppe in this case is my Table with Primary_Key LeistungGruppe_ID with Identity true and Identity increment 1 and seed 1.
I search a lot for this issue and alot of People got he same error.
They were told to simply set StoreGeneratedPattern to Identity and this would solve the Problem.
I tried it out and still got the same issue.
I'm new to the Entity Framework and have no idea about how to solve this Problem.
Somehow i think the model isn't updated probably because even if i Switch around These Settings I'm getting the same error over and over again.
Every help is appreciated.
You are trying to save an object to the database with an explicit ID set by you while the database is expecting to generate that value itself. That is the LeistungGruppe_ID property in your object is set to something other than 0 and it is not identified to the EF framework as an identity field. If you want the Id to be generated by the database as your post suggests, then the corresponding property in the Object should be decorated with the [Key] attribute.
If you are using the Fluent API then you should have something like this in your DBContext:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<LeistungGruppe>().Property(x => x.LeistungGruppe_ID).StoreGeneratedPattern = StoreGeneratedPattern.Identity;
base.OnModelCreating(modelBuilder);
}

Convert String to Int in LINQ to Entities

I am trying to duplicate the following SQL statement as a LINQ to Entities query (where "PRODUCTS" is the table mapped to the entity) ... NOTE IQueryable ... most of what I have seen posted as solutions convert either the search parameters, or the dump the results into an IEnumerable and then proceed to convert from there. I am dealing with 100's of millions of records and cannot afford to load 200 million records into memory, only to have to filter through them again. I would like, if possible to do this in a single query to the databse.
select *
from PRODUCTS
where
MODEL_CODE = '65' and
CAST(SERIAL_NUMBER as int) > 927000 and
CAST(SERIAL_NUMBER as int) < 928000
I have tried the following ...
int startSN, endSN;
startSN = 9500
endSN = 9500
if (!int.TryParse(startSerialNumber, out startSN))
throw new InvalidCastException("The start serial number was not a valid value");
if (!int.TryParse(endSerialNumber, out endSN))
throw new InvalidCastException("The end serial number was not a valid value");
IQueryable<PRODUCT> resultList = base.Context.PRODUCTS.Where(b =>
(Convert.ToInt32(b.SERIAL_NUMBER) > startSN) &&
(Convert.ToInt32(b.SERIAL_NUMBER) < endSN)).AsQueryable();
I have tried a couple of other version of things similiar to this with no luck. I have looked at the following posts also with no luck.
Convert string to int in an Entity Framework linq query and handling the parsing exception - the solution converts query to a list before converting the entity properties.
Convert string to Int in LINQ to Entities ? -
This problem was just with converting the parameters which can be easily done outside the LINQ to Entities statement. I am already doing this for the parameters.
LINQ to Entities StringConvert(double)' cannot be translated to convert int to string - This problem is actually the reverse of mine, trying to convert an int to a string. 1) SqlFunctions does not provide a function for converting TO an int. 2) Ultimately the solution is to, again convert to an IEnumerable before converting/casting the values.
Anybody got any other ideas? I am little stumped on this one!
Thank you,
G
If you don't use code-first, but an EDMX based approach model defined functions are probably the best solution: Convert String to Int in EF 4.0
Alternatively you can use...
base.Context.PRODUCTS.SqlQuery(string sql, params object[] parameters)
...and then pass in the raw SQL statement from your question.
DbSet<T>.SqlQuery(...) returns a DbSqlQuery<T> as result. It is important to keep in mind that this type does not implement IQueryable<T>, but only IEnumerable<T>. Its signature is:
public class DbSqlQuery<TEntity> : IEnumerable<TEntity>, IEnumerable, IListSource
where TEntity : class
So you can extend this result with further LINQ methods, but it is only LINQ to Objects that will be executed in memory with the returned result set from the SQL query. You can not extend it with LINQ to Entities that would be executed in the database. Hence, adding .Where filters to DbSqlQuery<T> does not have any influence on the database query and the set of data that is loaded from the DB into memory.
That's actually not surprising as it would mean otherwise that a partial expression tree (from a Where method) had to be translated into SQL and then merged into a hand-written SQL statement so that a correct new composed SQL statement results and could be sent to the database. Sounds like a pretty hard task to me.

How to affect the column order with Entity Framework Code First Migrations

I'm using Entity Framework 4.3 Code First and trying out the Migrations feature.
If I add a new property to my class and then run Add-Migration from the package manager console window I get something like this:
public override void Up()
{
AddColumn("Products", "Discontinued", c => c.Boolean(nullable: false));
}
I would like to be able to affect the order of the column as I don't want it to just be appended to the table but rather placed at a specific index. I thought I might be able to add it to my modelBuilder configuration, something like:
Property(p => p.Discontinued).HasColumnOrder(2);
but running Update-database does not appear to use it. Can this be done as a migration?
This is just a matter of missing functionality. SQL by itself does not rely on any implicit order of columns (with some exceptions: ORDER BY , ...).
Neither SQL Server nor ORACLE do have a direct SQL DDL command (aka ALTER TABLE...) to move a column around.
Therefore there's no possibility to change the order without high effort (recreate the table). See for example
How To change the column order of An Existing Table in SQL Server 2008
SQL SERVER – Change Order of Column In Database Tables
https://dba.stackexchange.com/questions/61978/how-to-change-the-column-order

Entity Framework. View return duplicate records

I use Entity Framework that contains view. And I have query:
var data = this.context.vwRevenues
.Where(x => x.revenue >= 0);
.OrderByDescending(x => x.year)
.ThenByDescending(x => x.month)
.Take(10)
.ToList();
This query returns set of entities, but 1st entity equals 5th.
data[0] == data[4] // true
I take sql script for this query from sql tracer and run it into SQL Management Studio,
it returns different records.
As per #Giovane Answer's
We had the same problem in our system with Entity Framework dealing with views. Try using ROW_NUMBER () OVER () SQL to create a column with unique values​​, but did not work.
I did the same thing but to make it work i need to open the EDMX model and then select a this column as an Entity Key.
Then it will work
There is a very good article on this
Duplicate Records
The articles most important line is:
When including a view in your Entity Model, the model seems to simply use the first not-nullable columns as primary key (as all columns used in the primary key should be non-nullable).
You only need to do: context.viewname.AsNoTracking().Where(x => x.ColumnName != null);
We had the same problem in our system with Entity Framework dealing with views. Try using ROW_NUMBER () OVER () SQL to create a column with unique values​​, but did not work.
We need to insert a field more, an FK for another table in the view so that it could add as an additional training for mebro EntityKeyMembers Elimite and so the problem of repetition.
Thus, if the problem persists in this kind of situation, the solution is to insert a FK column for it to be MEMBERS of the fields that form the EntityKey of the table.
In the view try converting the first record to a not null value, like this:
isnull(ROW_NUMBER() OVER (ORDER BY "Column"), 0) AS Row
It indicates to the Entity Framework that can be the primary key automatically.
If you don't want to update edmx and set any key to column &&
if you don't want to update view record (only for get record) then use below code its working for me.
context.viewname.MergeOption = System.Data.Objects.MergeOption.NoTracking;
context.viewname.Where(x => x.columnname != null);