linq2db / linq2db.EntityFrameworkCore

Bring power of Linq To DB to Entity Framework Core projects
MIT License
449 stars 39 forks source link
dotnet-core entity-framework entity-framework-core entityframeworkcore linq linq2db orm sql

linq2db.EntityFrameworkCore

linq2db.EntityFrameworkCore is an integration of LINQ To DB with existing EntityFrameworkCore projects. It was inspired by this issue in EF.Core repository.

Build status

Azure DevOps builds Azure DevOps builds Azure DevOps builds

Feeds

Unique features

In your code you need to initialize integration using following call:

LinqToDBForEFTools.Initialize();

After that you can just call DbContext and IQueryable extension methods, provided by LINQ To DB.

You can also register additional options (like interceptors) for LinqToDB during EF context registration, here is an example:

var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionsBuilder.UseSqlite();
optionsBuilder.UseLinqToDB(builder =>
{
    // add custom command interceptor
    builder.AddInterceptor(new MyCommandInterceptor());
    // add additional mappings
    builder.AddMappingSchema(myCustomMappings);
    // configure SQL Server dialect explicitly
    builder.AddCustomOptions(o => o.UseSqlServer(SqlServerVersion.v2022));
});

There are many extensions for CRUD Operations missing in vanilla EF (watch our video):

// fast insert big recordsets
ctx.BulkCopy(new BulkCopyOptions {...}, items);

// query for retrieving products that do not have duplicates by Name
var query =
    from p in ctx.Products
    from op in ctx.Products.LeftJoin(op => op.ProductID != p.ProductID && op.Name == p.Name)
    where Sql.ToNullable(op.ProductID) == null
    select p;

// insert these records into the same or another table
query.Insert(ctx.Products.ToLinqToDBTable(), s => new Product { Name = s.Name ... });

// update these records by changing name based on previous value
query.Update(prev => new Product { Name = "U_" + prev.Name ... });

// delete records that matched by query
query.Delete();

Some extensions require LINQ To DB ITable<T> interface, which could be acquired from DbSet<T> using ToLinqToDBTable() extension method.

For ITable<T> interface LINQ To DB provides several extensions that may be useful for complex databases and custom queries:

table = table.TableName("NewTableName");     // change table name in query
table = table.DatabaseName("OtherDatabase"); // change database name, useful for cross database queries.
table = table.OwnerName("OtherOwner");       // change owner.

// inserting into other existing table Products2
query.Insert(ctx.Products.ToLinqToDBTable().TableName("Products2"), s => new Product { Name = s.Name ... });

It is not required to work directly with LINQ To DB DataConnection class but there are several ways to do that. LINQ To DB will try to reuse your configuration and select appropriate data provider:

// uing DbContext
using (var dc = ctx.CreateLinqToDBConnection())
{
   // linq queries using linq2db extensions
}

// using DbContextOptions
using (var dc = options.CreateLinqToDBConnection())
{
   // linq queries using linq2db extensions
}

You can use all LINQ To DB extension functions in your EF linq queries. Just ensure you have called ToLinqToDB() function before materializing objects for synchronous methods.

Since EF Core have defined it's own asynchronous methods, we have to duplicate them to resolve naming collisions. Async methods have the same name but with LinqToDB suffix. E.g. ToListAsyncLinqToDB(), SumAsyncLinqToDB(), ect. The same methods are added whe you need EF Core query processing but there is collision with LINQ To DB and they have extensions with EF suffix - ToListAsyncEF(), SumAsyncEF(), ect.

using (var ctx = CreateAdventureWorksContext())
{
    var productsWithModelCount =
        from p in ctx.Products
        select new
        {
            // Window Function
            Count = Sql.Ext.Count().Over().PartitionBy(p.ProductModelID).ToValue(),
            Product = p
        };

    var neededRecords =
        from p in productsWithModelCount
        where p.Count.Between(2, 4) // LINQ To DB extension
        select new
        {
            p.Product.Name,
            p.Product.Color,
            p.Product.Size,
            // retrieving value from column dynamically
            PhotoFileName = Sql.Property<string>(p.Product, "ThumbnailPhotoFileName")
        };

    // ensure we have replaced EF context
    var items1 = neededRecords.ToLinqToDB().ToArray();       

    // async version
    var items2 = await neededRecords.ToLinqToDB().ToArrayAsync(); 

    // and simple bonus - how to generate SQL
    var sql = neededRecords.ToLinqToDB().ToString();
}

Also check existing tests in test project for some examples.

Why should I want to use it?

There are many reasons. Some of them:

Current status

Below is a list of providers, that should work right now:

Known limitations

Help! It doesn't work!

If you encounter any issue with this library, first check issues to see if it was already reported and if not, feel free to report new issue.