Skip to content

Save entire entity graphs to EntityFramework >= 5 from any DTO or dicctionary. It's like saving a MongoDB document.

Notifications You must be signed in to change notification settings

khoavn/Detached

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Detached 5

What is it

Hi!, I'm trying to build a fast, object-oriented mapper to simplify the task of saving disconected entities in web services. It was heavily inspired by GraphDiff, AutoMapper and other awesome libraries. Any fix or feedback is very welcome.

Note: It works with Preview 8 of .net 5.

What does it solve

Disconected entities

When working with EntityFramework, NHibernate, or practically any other ORM that tracks entity states, there is a problem when persinsing non-tracked entities that comes usually from a deserialization (request, response, a file, another db, etc.) EF has the TrackGraph method, but the state of each entity needs to be specified manually. NHibernate has a Merge feature, but it merges everything, and sometimes there are associated entities that should not be modified.

Data transfer objects (DTOs)

To reduce data traffic and, improve the security and the readability of swagger models, many times DTOs are used, and often they are very similar to the original entity.

A manual mapping or a tool like AutoMapper is needed to convert the DTOs to an entity, before attach them to the ORM context/session.

Partial updates

C# does not support an "Undefined" value, like JavaScript, that's why for partial updates, Null may be used for optional values. But in some cases, null needs to be persisted, to remove a previous field value or disconnect an existing relationship.

How it works

The main method of Detached is MapAsync. It receives an object or dictionary as a parameter, then loads the corresponding entity from the database (like GraphDiff and Merge), copies the values from the given DTO and set the correct states.

Entities and DTOs

Detached can handle DTO-Entity, DTO-DTO and Entity-Entity mapping. There are important tasks when mapping entities, like checking back references and merging collections. So that, classes representing entities should be marked with the [Entity] attribute or configured with modelOptions.Configure<Entity>().IsEntity(). For DetachedDbContext (EF) entities are marked automatically when the correspondng DbSet exists. More info on configuration, later in this doc.

Compositions and Aggregations

Aggregations are the weak relations (B and A are independent), also known as "Has a". Compositions are the strong (B cannot exists without A), also known as "Owns a".

This can be configured using the [Aggregation] and [Composition] attributes on the corresponding properties or fluently using modelOptions.Configure<Entity>().Member(e => e.Member).Composition(). More info on configuration, later in this doc.

When mapping, detached will only modify the root entity (the one passed as a parameter) and all the related entities marked as compositions. Associations are just attached an marked as Unmodified.

Partial updates

Detached copies only the properties that match (or are configured) from the DTO to the Entity, other properties are not overwritten. For partial updates, a DTO with the needed properties may be created, or a Dictionary<string, object> can be passed as a parameter. It also maps anonymous objects.

Partial updates without manually creating new types are supported using the IPatch interface, the PatchProxyFactory and the PatchJsonConverter. IPatchs indicates when properties are dirty, and PatchJsonConverter can generate proxies of the classes that auto-implement IPatch and deserialize json using that proxy. More info on configuration, later in this doc.

Getting Started

Install Nu-Get package
Install-Package Detached.EntityFramework -Version 5.0.0
Inherit from DetachedDbContext
public class TestDbContext : DetachedDbContext
{
    ...
}
Register dependencies

DetachedDbContext needs a QueryProvider instance, that helps loading the current status of the entities to save and a Mapper instance that copies the given DTO/Entity state over the current entities. If working on ASP.NET, this is usually configured in the DI container at the beggining (Startup.cs).

public void ConfigureServices(IServiceCollection services)
{
    services.AddDetachedEntityFramework(); // Mapper & QueryBuilder.
    services.AddDbContext<MainDbContext>(cfg =>
    {
        ...
    });
}
Define model and DTO

As mentioned before, entities (the classes associated to the DbContext) should be tagged. That can be done by adding the [Entity] attribute to the class, or configuring the mapper options.

[Entity]
public class User
{
    public Guid Id { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<ModelOptions>(m =>
    {
        m.Configure<User>().IsEntity();
    });
}

DTOs don't need any special configuration and are mapped by convention using the same property names.

Perform a Map

Once the DetachedDbContext is configured and initialized somewhere, the MapAsync method can be called to load current state and copy DTO values. MapAsync returns the persisted entity (that comes from the DB) with the updated values. No updates are persisted until SaveChanges is called().

User attachedUser = await dbContext.MapAsync<User>(new UserDTO { Id = 1, Name = "NewName" });
await dbContext.SaveChangesAsync();
Configure Entities

// TODO:

About

Save entire entity graphs to EntityFramework >= 5 from any DTO or dicctionary. It's like saving a MongoDB document.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C# 100.0%