EfCore.GenericServices 2.0.0

Library to help you quickly code Create, Read, Update and Delete (CRUD) accesses in web/mobile/desktop applications. It acts as a adapter between a database accessed by Entity Framework Core (EF Core) and the needs of the front-end system.

Install-Package EfCore.GenericServices -Version 2.0.0
dotnet add package EfCore.GenericServices --version 2.0.0
paket add EfCore.GenericServices --version 2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

EfCore.GenericServices

This library helps you quickly code Create, Read, Update and Delete (CRUD) accesses for a web/mobile/desktop application. It acts as a adapter and command pattern between a database accessed by Entity Framework Core (EF Core) and the needs of the front-end system.

This library takes advantage of the fact that each of the four CRUD database accesses differ in what they do, but they have a common set of data part they all use, which are:
a) What database class/table do you want to access?
b) What properties in that class/table do you want to access or change?

This library uses DTOs (data transfer objects), also known as ViewModels, plus a special interface to define the class/table and the properties to access.
That allows the library to implement a generic solution for each of the four CRUD accesses, where the only thing that changes is the DTO you use.

Typical web applications have hundreds of CRUD pages - display this, edit that, delete the other -
and each CRUD access has to adapt the data in the database to show the user, and then apply the changes to the database.
So, you create one set of update code for your specific application and then cut/paste + change one line (the DTO name) for all the other versions.

I personally work with ASP.NET Core, so my examples are from that, but it will work with any NET Core type of application
(I do know one person have used this libary with WPF).

NuGet link and link to documentation.

MIT license.

Code examples of using EfCore.GenericServices

I personally work with ASP.NET Core, so my examples are all around ASP.NET Core, but EfCore.GenericServices will work with any NET Core type of application
(I do know one person have used this libary with WPF).

ASP.NET Core MVC - razor pages

The classic way to produce HTML pages in ASP.NET is using the MVC approach, with razor pages.
Here a simple example to show you the basic way to inject and then call the ICrudServices, in this case a simple List.

public class BookController

    private ICrudServices _service;

    public BookController(ICrudServices service)
    {
        _service = service;
    }

    public ActionResult Index()
    {
        var dataToDisplay = _service.ReadManyNoTracked<BookListDto>().ToList()
        return View(dataToDisplay);
    }
    //... etc.

ASP.NET Core MVC - razor pages

Here is the code from the example Razor Page application contained in this repo for adding a review to a Book (the example site is a tiny Amazon-like site).
This example shows an more complex example where I am updating the Book class that uses a Domain-Driven Design (DDD) approach
to add a new review to a book. The code shown is the complete code in the AddReview.cshtml.cs class.

public class AddReviewModel : PageModel
{
    private readonly ICrudServices _service;

    public AddReviewModel(ICrudServices service)
    {
        _service = service;
    }

    [BindProperty]
    public AddReviewDto Data { get; set; }

    public void OnGet(int id)
    {
        Data = _service.ReadSingle<AddReviewDto>(id);
        if (!_service.IsValid)
        {
            _service.CopyErrorsToModelState(ModelState, Data, nameof(Data));
        }
    }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
        _service.UpdateAndSave(Data);
        if (_service.IsValid)
            return RedirectToPage("BookUpdated", new { message = _service.Message});

        //Error state
        _service.CopyErrorsToModelState(ModelState, Data, nameof(Data));
        return Page();
    }
}

NOTE: If you compare the code above with the
AddPromotion or
ChangePubDate
updates in the same example then you will see they are identical apart from the type of the Data property.

And the ViewModel/DTO isn't anything special (see the
AddReviewDto).
They just need to be marked with an empty
ILinkToEntity&lt;TEntity&gt; interface, which tells GenericServices which EF Core entity
class to map to. For more security you can also mark any read-only properties with the
[ReadOnly(true)] attribute - GenericServices will never try to update the
database with any read-only marked property.

ASP.NET Web API

When using ASP.NET Web API then another companion library called EfCore.GenericServices.AspNetCore
provides extension methods to help return the data in the correct form (plus other methods to allow unit testing of Web API actions using EfCore.GenericServices).

The code below comes from the example ToDoController
example in the EfCore.GenericServices.AspNetCore GitHub repo.

public class ToDoController : ControllerBase
{

    [HttpGet]
    public async Task<ActionResult<WebApiMessageAndResult<List<TodoItem>>>> GetManyAsync([FromServices]ICrudServices service)
    {
        return service.Response(await service.ReadManyNoTracked<TodoItem>().ToListAsync());
    }

    [Route("name")]
    [HttpPatch()]
    public ActionResult<WebApiMessageOnly> Name(ChangeNameDto dto, [FromServices]ICrudServices service)
    {
        service.UpdateAndSave(dto);
        return service.Response();

    //... other action methods removed 
}

EfCore.GenericServices

This library helps you quickly code Create, Read, Update and Delete (CRUD) accesses for a web/mobile/desktop application. It acts as a adapter and command pattern between a database accessed by Entity Framework Core (EF Core) and the needs of the front-end system.

This library takes advantage of the fact that each of the four CRUD database accesses differ in what they do, but they have a common set of data part they all use, which are:
a) What database class/table do you want to access?
b) What properties in that class/table do you want to access or change?

This library uses DTOs (data transfer objects), also known as ViewModels, plus a special interface to define the class/table and the properties to access.
That allows the library to implement a generic solution for each of the four CRUD accesses, where the only thing that changes is the DTO you use.

Typical web applications have hundreds of CRUD pages - display this, edit that, delete the other -
and each CRUD access has to adapt the data in the database to show the user, and then apply the changes to the database.
So, you create one set of update code for your specific application and then cut/paste + change one line (the DTO name) for all the other versions.

I personally work with ASP.NET Core, so my examples are from that, but it will work with any NET Core type of application
(I do know one person have used this libary with WPF).

NuGet link and link to documentation.

MIT license.

Code examples of using EfCore.GenericServices

I personally work with ASP.NET Core, so my examples are all around ASP.NET Core, but EfCore.GenericServices will work with any NET Core type of application
(I do know one person have used this libary with WPF).

ASP.NET Core MVC - razor pages

The classic way to produce HTML pages in ASP.NET is using the MVC approach, with razor pages.
Here a simple example to show you the basic way to inject and then call the ICrudServices, in this case a simple List.

public class BookController

    private ICrudServices _service;

    public BookController(ICrudServices service)
    {
        _service = service;
    }

    public ActionResult Index()
    {
        var dataToDisplay = _service.ReadManyNoTracked<BookListDto>().ToList()
        return View(dataToDisplay);
    }
    //... etc.

ASP.NET Core MVC - razor pages

Here is the code from the example Razor Page application contained in this repo for adding a review to a Book (the example site is a tiny Amazon-like site).
This example shows an more complex example where I am updating the Book class that uses a Domain-Driven Design (DDD) approach
to add a new review to a book. The code shown is the complete code in the AddReview.cshtml.cs class.

public class AddReviewModel : PageModel
{
    private readonly ICrudServices _service;

    public AddReviewModel(ICrudServices service)
    {
        _service = service;
    }

    [BindProperty]
    public AddReviewDto Data { get; set; }

    public void OnGet(int id)
    {
        Data = _service.ReadSingle<AddReviewDto>(id);
        if (!_service.IsValid)
        {
            _service.CopyErrorsToModelState(ModelState, Data, nameof(Data));
        }
    }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
        _service.UpdateAndSave(Data);
        if (_service.IsValid)
            return RedirectToPage("BookUpdated", new { message = _service.Message});

        //Error state
        _service.CopyErrorsToModelState(ModelState, Data, nameof(Data));
        return Page();
    }
}

NOTE: If you compare the code above with the
AddPromotion or
ChangePubDate
updates in the same example then you will see they are identical apart from the type of the Data property.

And the ViewModel/DTO isn't anything special (see the
AddReviewDto).
They just need to be marked with an empty
ILinkToEntity&lt;TEntity&gt; interface, which tells GenericServices which EF Core entity
class to map to. For more security you can also mark any read-only properties with the
[ReadOnly(true)] attribute - GenericServices will never try to update the
database with any read-only marked property.

ASP.NET Web API

When using ASP.NET Web API then another companion library called EfCore.GenericServices.AspNetCore
provides extension methods to help return the data in the correct form (plus other methods to allow unit testing of Web API actions using EfCore.GenericServices).

The code below comes from the example ToDoController
example in the EfCore.GenericServices.AspNetCore GitHub repo.

public class ToDoController : ControllerBase
{

    [HttpGet]
    public async Task<ActionResult<WebApiMessageAndResult<List<TodoItem>>>> GetManyAsync([FromServices]ICrudServices service)
    {
        return service.Response(await service.ReadManyNoTracked<TodoItem>().ToListAsync());
    }

    [Route("name")]
    [HttpPatch()]
    public ActionResult<WebApiMessageOnly> Name(ChangeNameDto dto, [FromServices]ICrudServices service)
    {
        service.UpdateAndSave(dto);
        return service.Response();

    //... other action methods removed 
}

Release Notes

- New feature: Now handles EF Core's DbQuery type (DbQuery type is only used for reads) - fixes Issue #16.
- New Feature: Added `ProjectFromEntityToDto{TEntity,TDto}` to the services. This allows you to read data with a query prior to the projection to a DTO. Fixes issue #10 and #15
- New Feature: Added `IGenericStatus BeforeSaveChanges(DbContext)` to configuration.
This allows you to inject code that is called just before SaveChanges/SaveChangesAsync is run. This allows you
to add some validation, logging etc. - see issue #14.
- Improvement: Previously the Sql error handler was only used if validation was turned on.
Now, if the SaveChangesExceptionHandler property is not null, then taht method is called,
i.e. it is not longer dependant on the state of the ...ValidateOnSave flag in the config.  
- Breaking change (Minor): In version 1.3.1 both `DeleteAndSave` and `DeleteWithActionAndSave` used `IgnoreQueryFilters` to get all entities.
This was done so that soft deleted items would be found, but its dangerous in multi-tenant systems.
In 2.0.0 only `DeleteWithActionAndSave` will use `IgnoreQueryFilters` to get all entities. That is safer, as you can provide extra checks in the method you provide.
- Performance bug fix: There was a performance issue when using the setup methods use in unit testing and non-DI situations

Version History

Version Downloads Last updated
2.0.0 467 1/2/2019
2.0.0-preview001 225 12/24/2018
1.3.3 455 10/27/2018
1.3.2 309 10/22/2018
1.3.1 353 9/26/2018
1.3.0 308 9/18/2018
1.2.6 925 8/18/2018
1.2.4 237 7/29/2018
1.2.3 138 7/24/2018
1.2.2 120 7/16/2018
1.2.1 116 7/14/2018
1.1.0 190 6/7/2018
1.0.0 266 4/12/2018