I am using .NET Core 3.1, Microsoft.EntityFrameworkCore 3.1.9 and Npgsql 4.1.9. ProductRelease database entity has a version column - a string of numbers separated by a dot in major.minor.build.revision format. I would like to retrieve all the records where version <= 12.5.3.102197.
[Table("product_release", Schema = "test")]
public partial class ProductRelease
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("version")]
[StringLength(32)]
public string Version { get; set; }
}
I found a question where it is explained how to do it in raw SQL with string_to_array built-in function like this:
SELECT
*
FROM
test.product_release pr
WHERE
string_to_array(pr.version, '.')::int[] <= string_to_array('12.5.3.102197', '.')::int[];
How can I translate this query to EF Core (Npgsql). Is there a way to call string_to_array function from EF Core? I would like to avoid writing raw SQL queries.
Related
Supposedly, I have a simple DbContext with Blog and Post models:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Let's say I have a stored procedure that returns some DTO:
[Keyless]
public class BlogPostDto
{
public string PostTitle { get; init; }
public string BlogName { get; init; }
}
Today I put the following into DbContext:
public class AppDbContext : DbContext {
public virtual DbSet<BlogPostDto> NeverUseIt { get; set; }
partial void OnModelCreatingPartial(ModelBuilder modelBuilder) {
modelBuilder.Entity<BlogPostDto>().ToView(null);
}
}
And then I can get Stored Procedure results shaped in the way I want:
List<BlogPostDto> results = await db.Set<BlogPostDto>().FromSqlRaw($"EXEC MyProc").ToListAsync();
So, my question is, do I have to add my BlogPostDto into DbContext? I know that in EF Core 3 I did; but there were a large number of improvements since then. Creating a bogus DbSet and mapping it to non-existent view just feels counter-intuitive!
The closest I found in most current documentation is here. The very first example of context is Serving as the return type for raw SQL queries. - but the article assumes that I have a matching view already in the database.
UPDATE: It looks like ToView(null) is not necessary - just DbSet<>
Nothing has changed in that regard so far from what you see in the EF Core 6.0 documentation and SO posts you are referring to.
Just to be crystal clear, you don't need a DbSet<T> returning property in your context. But you do need to include the type (keyless or not) in the model using the modelBuilder.Entity<T>() call, and also ToView(null) to prevent EF Core migrations associate database table and/or view with it, and optionally HasNoKey() in case you don;t want to use EF Core dependent attributes like [Keyless] in your data classes.
So the minimum requirement for your example is this line
modelBuilder.Entity<BlogPostDto>().ToView(null);
Now, this is a long time requested feature (which exists in the "obsolete" EF6 which the "modern" EF Core is supposed to replace), tracked by Support raw SQL queries without defining an entity type for the result #10753 issue in EF Core issue tracker. It was initially planned to be included in the upcoming EF Core 7.0 release (Nov 2022), but later has been cut for (eventually) EF Core 8.0 (Nov 2023). So until then you have to use the "register model" approach, or use 3rd party library(!) like Dapper for the same task, as suggested by one of the EF Core team members(?!).
I use EF Core - Database.EnsureCreated to create table by C# class, but now I'd like to get only creating table and columns SQL and without sending request to database.
e.g
public class ApplicationDbContext : DbContext
{
public DbSet<Dto> Dtos{ get; set; }
}
public class Dto
{
[Key]
[StringLength(10)]
public string ID{ get; set; }
[StringLength(100)]
[Required]
public string Name{ get; set; }
[Required]
public int Age{ get; set; }
}
db.Database.EnsureDeleted();
Expected result :
create table Dto
{
ID nvarchar(10) primary key not null,
Name nvarchar(100) not null,
Age int not null
}
What I tried?
I did open SSMS profile and get SQL from EF Core, but it needs to send request to database.
My usual approach to these types of questions is to poke around in the source code. I found the implementation of .EnsureCreated(), then noticed that there is a public method to .GenerateCreateScript() on the same type. Searching the entire code base for calls to that method reveals an extension method on DatabaseFacade. So all you need to do is;
var sqlSource = db.Database.GenerateCreateScript();
However using google to search for that method doesn't reveal any results in the official documentation. The "proper" way to create a production database is to create migrations, then create an sql script to apply them.
You can use the dotnet EF CLI to run the migrations script:
dotnet ef migrations script -i -o "C:\MYFOLDER\dbscript.sql"
REF: https://learn.microsoft.com/en-us/ef/core/cli/dotnet
I'm using Pomelo.EntityFrameworkCore.MySql (3.1.1) with my MySql database.
I'm running the following query:
var model = await _dbContext.Model.FirstOrDefaultAsync(x => x.MyString == myString);
Model Definition:
[Table("model")]
public class Model
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("my_string", TypeName = "varchar(36)")]
public string MyString { get; set; }
[Column("status")]
public string Status { get; set; }
}
The Model table only has 1 record. However, this query takes a whopping 2+ seconds... this seems a little long for such a small simple query. Have I mis-configured something somewhere?
Pomelo and MySQL do not run truly async queries.
We are currently performance tuning against EFCore 3.1 and Pomelo.EFCore 3.1. One of our more complicated queries runs a full 2 seconds faster running sync (10609-11126ms) vs async (13073ms).
I am using the Entity Framework Core to interface my SQLite database. I want to set a minimum and maximum range for the field ExitSide in my model Station:
using System.ComponentModel.DataAnnotations;
...
class Station
{
public int StationId { get; set; }
[Range(0,2)]
public int ExitSide { get; set; }
}
But somehow it seems to get ignored. I am still able to create Stations with ExitSide=5 for example. What am I doing wrong?
EF Core does not currently recognise the Range data annotation
I have such code using EntityFramework Alpha3 (from nuget):
class Member
{
[Key]
public int Key { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string Sitename { get; set; }
public DateTime? RegDate { get; set; }
}
class MembersContext : DbContext
{
public MembersContext()
: base("Name=ConnectionString")
{
}
public DbSet<Member> Members { get; set; }
}
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
Database.SetInitializer<MembersContext>(null);
const int memberKey = 1001;
using (var db = new MembersContext())
{
var query = from m in db.Members
where
m.Key == memberKey
&& EntityFunctions.DiffDays(m.RegDate, DateTime.Now) > 0
select m;
var member = query.FirstOrDefault();
}
return View();
}
}
I have tried to use EntityFunctions in fresh ASP.NET MVC project (based on .net 4.5) - it always fails with error:
LINQ to Entities does not recognize the method 'System.Nullable`1[System.Int32] DiffDays(System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime])' method, and this method cannot be translated into a store expression.
Same code works completely fine in Console app. Any ideas, what's wrong?
I suspect that in the case of your ASP.NET app, you also have a reference to System.Data.Entity, and that you are using the EntityFunctions class defined System.Data.Entity.
However, Entity Framework 6 has removed the dependency on System.Data.Entity and has redefined all necessary classes for that purpose inside of the EntityFramework assembly.
Meaning that, in the case of your console app, I am guessing that either by design (System.Data.Entity is not referenced) or by accident (System.Data.Entity is referenced but the EntityFunctions class is taken from EntityFramework.dll), the correct version of EntityFunctions (from EntityFramework.dll) is taken.
If you are using any version of Entity Framework 6, make sure that you are using the EntityFunctions class that can be found in EntityFramework.dll, not the one in System.Data.Entity. Source code of EntityFunctions.cs in Entity Framework 6
Actually, if you use Entity Framework 6, I would recommend removing all and any references to System.Data.Entity - in order to avoid any future confusion and mistake.
The reason why you're getting this error is that by design Linq to Entities converts all expression to server query. So it does not know how to translate DiffDays to SQL. Try to omit this method and rewrite expression.
UPDATE:
EntityFunctions are Canonical functions so you can use them in Linq to Entities. So the only reasonable explanation is in EF Alhpa version.
Resolved this by changing all my EntityFunctions.TruncateTime to DbFunctions.TruncateTime and the error went away after examining various using statements and removing all of those that referenced System.Data.