HenkKin / EntityCloner.Microsoft.EntityFrameworkCore

Cloning entities using Microsoft.EntityFrameworkCore
21 stars 3 forks source link

EntityCloner.Microsoft.EntityFrameworkCore

Build Status NuGet NuGet

Summary

Cloning entities using EntityFrameworkCore configuration.

This library is Cross-platform, supporting net6.0, net7.0 and net8.0.

Installing EntityCloner.Microsoft.EntityFrameworkCore

You should install EntityCloner.Microsoft.EntityFrameworkCore with NuGet:

Install-Package EntityCloner.Microsoft.EntityFrameworkCore

Or via the .NET Core command line interface:

dotnet add package EntityCloner.Microsoft.EntityFrameworkCore

Either commands, from Package Manager Console or .NET Core CLI, will download and install EntityCloner.Microsoft.EntityFrameworkCore and all required dependencies.

Dependencies

Usage

This package provides two extension methods for DbContext:

These extension methods supports cloning an entity. Be aware: the cloned entity is not tracked by the ChangeTracker of EntityFrameworkCore. You should add it manually to the DbSet.

Only clone the entities you want to clone

If you provide IncludeQuery configuration, then the included entities will be cloned too. The Include and ThenInclude works same way as EntityFrameworkCore for querying with Linq.

Based on EntityFrameworkCore configuration Model

It will clone the entity with related entities based on EntityFrameworkCore configuration model.

Resetting properties to default value

During cloning, all PrimaryKey, ForeignKey (except for some exceptions) and ConcurrencyToken properties will be reset to default values

If a ForeignKey entity is not included, then the ForeignKey property will not be reset. This is for example in cases when you have a ForeignKey to an entity from a selection list.

Support for Composite PrimaryKeys

It also supports primarty keys based on multiple properties.

Support for IQueryable and (list of) plain entities

It also supports plain classes, IQueryable and all type of lists like T[], IEnumerables, IList ICollection and the implementation variants of it.

The only requirement is, that the entity should be part of the model configuration of EntityFrameworkCore.

To use it:

...
using EntityCloner.Microsoft.EntityFrameworkCore.EntityFrameworkCore.SqlServer;

public class YourClass
{

    // This method gets called by the runtime. Use this method to add services to the container.
    public async Task YourMethod(DbContext dbContext)
    {
        var entityId = 10;

        // To clone only the entity:
        var clonedOrderEntity = await dbContext.CloneAsync<Order>(entityId);

        // To clone entity with related data
        var clonedOrderEntityWithRelatedEntities = await dbContext.CloneAsync<Order>(
            includeQuery => includeQuery
                .Include(o => o.OrderLines)
                    .ThenInclude(ol => ol.Discounts)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.CustomerAddresses)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.Invoices) 
                        .ThenInclude(x => x.InvoiceLines),
            entityId);

        // To clone using IQueryable
        var entityId = 10;
        var query = DbSet<TestEntity>.AsNoTracking()
                .Include(o => o.OrderLines)
                    .ThenInclude(ol => ol.Discounts)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.CustomerAddresses)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.Invoices) 
                        .ThenInclude(x => x.InvoiceLines)
                .Where(o => o.Id == entityId);

        var clonedOrderEntityViaQueryable = await dbContext.CloneAsync(query);

        // To clone using entity
        var entityId = 10;
        var entity = await DbSet<TestEntity>.AsNoTracking()
                .Include(o => o.OrderLines)
                    .ThenInclude(ol => ol.Discounts)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.CustomerAddresses)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.Invoices) 
                        .ThenInclude(x => x.InvoiceLines)
                .Where(o => o.Id == entityId)
                .SingleAsync();

        var clonedOrderEntityViaEntity = await dbContext.CloneAsync(entity);

        // To clone using list of entities
        var entityId = 10;
        var entities = await DbSet<TestEntity>.AsNoTracking()
                .Include(o => o.OrderLines)
                    .ThenInclude(ol => ol.Discounts)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.CustomerAddresses)
                .Include(o => o.Customer)
                    .ThenInclude(x => x.Invoices) 
                        .ThenInclude(x => x.InvoiceLines)
                .Where(o => o.Id == entityId)
                .ToListAsync();

        var clonedOrderEntitiesViaList = await dbContext.CloneAsync(entities);
    }
}

Debugging

If you want to debug the source code, thats possible. SourceLink is enabled. To use it, you have to change Visual Studio Debugging options:

Debug => Options => Debugging => General

Set the following settings:

[  ] Enable Just My Code

[X] Enable .NET Framework source stepping

[X] Enable source server support

[X] Enable source link support

Now you can use 'step into' (F11).