EF.Core.Repository
Simple repository for Ef.Core with basic CRUD functionality The reason I implemented this is because I found myself re-writing basic CRUD functionality over and over again. Most of the time:
- use
.include(...)
to include eager load associated entities - update (add/delete/update) children properties
Using repository pattern with entity framework enforces a consistent convention and that is what this library is aiming towards.
Basic setup
- Entity should have Id property. Using
[Key]
is optional IF Id property does not follow common naming convention (i.e.id
or_id
, case insensitive)
public class DummyModel
{
public string Name { get; set; }
public List<Nested> Children { get; set; }
}
- Create profile which is used to update an entity given a DTO
public class DummyModelProfile : EntityProfile<DummyModel>
{
public override void Update(DummyModel entity, DummyModel dto)
{
entity.Name = dto.Name;
// ModifyList will try to add/delete entities based on Id based on whether they
// appear in dto.Children or not
ModifyList(entity.Children, dto.Children, x => x.Id);
}
// Intercept IQueryable to include related entities
public override IQueryable<DummyModel> Include<TQueryable>(TQueryable queryable) where TQueryable : IQueryable<DummyModel>
{
return queryable.Include(x => x.Children);
}
}
- OR use an auto mapper functionality to map properties. Be careful when using auto mapper. I recommend using manual mapper for more control.
public class DummyModelProfile : EntityProfile<DummyModel>
{
public DummyModelProfile()
{
// Map indivisually
Map(x => x.Name);
Map(x => x.Children);
// OR
MapAll();
}
// Intercept IQueryable to include related entities
public override IQueryable<DummyModel> Include<TQueryable>(TQueryable queryable) where TQueryable : IQueryable<DummyModel>
{
return queryable.Include(x => x.Children);
}
}
- Register dependency via
IServiceCollection
extension
var serviceProvider = services
.AddEfRepository<EntityDbContext>(options => options
.Profile(Assembly.GetExecutingAssembly()));
- Use
IBasicCrud
IEfRepository repo = ... // DI inject IEfRepository
// Get IBasicCrud instance
IBasicCrud<DummyModel> = repo.For<DummyModel>();
- Available methods in
IBasicCrud
// Get all entities given an array of Ids
Task<IEnumerable<TSource>> GetAll<TId>(param TId[]);
// Get all entities given a filter expression
Task<IEnumerable<TSource>> GetAll(params Expression<Func<TSource, bool>>[]);
// Boolean if there is any
Task<bool> Any(params Expression<Func<TSource, bool>>[]);
// Get single entity by Id
Task<TSource> Get<TId>(TId);
// Get single entity given a filter expression
Task<TSource> Get(params Expression<Func<TSource, bool>>[]);
// Save one or more entities
Task<IEnumerable<TSource>> Save(TSource[]);
// Update entity by Id
Task<TSource> Update<TId>(TId, TSource);
// Update entity manually
Task<TSource> Update<TId>(TId, Action<TSource>);
// Update entity by filter expression
Task<TSource> Update(TSource, params Expression<Func<TSource, bool>>[]);
// Update entity manually by filter expression
Task<TSource> Update(Action<TSource>, params Expression<Func<TSource, bool>>[]);
// Delete entity by Id
Task<IEnumerable<TSource>> Delete<TId>(TId[]);
// Delete entity given a filter expression
Task<TSource> Delete(params Expression<Func<TSource, bool>>[]);
// This is useful if you want to defer SaveChanges in session mode
// Changes are not automatically saved back to DbContext in session mode
IBasicCrud<TSource, TId> Delayed();
// This is useful if you want don't want to include all related entities
// or turn entities into a "god" object
IBasicCrud<TSource, TId> Light();
Notes:
- "Id" has a type constraint of
: struct
, which means it accepts all primitive types includingGUID
andString
. - Including all associated entities will lead to "god object" which is anti-pattern. However, you can use
LightWeight
session to not include all dependent properties.