C# Repository Pattern? Complex queries still placed inside repository? - c#

I am learning the Repository Pattern. I have managed to write the code to do it.
I read the article say each Repository related to one entities.
This is what i have for my Repository:
public class ProductRepository : BaseProductRepository
{
public ProductRepository(StoreDBContext context) : base(context)
{
this.context = context;
}
public override async Task<IEnumerable<TResult>> GetTopProducts<TResult>(Func<Product, TResult> selectFunc)
{
var products = this.context.Product
.Select(selectFunc)
.ToList();
return products;
}
}
I am having the generic and expression in the method is because I want to have the caller to define what result should be e.g. partial data from product instead of all the data of a product.
Now i encounter an issue. What if the query is little complex which e.g. more table to join and the resulting data is returning not only the data of the Product, may be the number of users purchased the product etc.
Where can those complex query's which return complex dataset would be?

TResult should be in the 'shape' that the method relates to. It certainly shouldn't be a database entity.
In your case TResult for GetTopProducts would be a collection of product summaries.
class GetTopProductsResult
{
public IEnumerable<Product> TopProducts {get;set;}
public class Product
{
public int Id {get;set;}
public string Name {get;set;}
public int UnitsSold {get;set;}
}
}
I also suggest you look at AutoMapper which will basically implement your Select function in a more structured way.

Related

When creating an instance from an api call, how to increment an attribute of another entity in dotnet ef?

In an api I have built using dotnet 3.1, I have the Data access model for entity "Quotation" as follows.
public class QuotationDao : BaseEntity
{
public double Amount { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public string Status { get; set; }
public Guid CustomerId { get; set; }
public CustomerDao Customer { get; set; }
public virtual ICollection<QuoteItemDao> Items { get; set; }
}
When I create a new instance of the entity 'QuoteItem', I want to increase the 'amount' attribute of the 'Quotation' entity by 1. And update the database.
The implementation of the BaseController.cs is shown below.
public class BaseController<TDao, TCreateRq, TUpdateRq, TResponse> : ControllerBase
where TDao : BaseEntity where TResponse : BaseResponse
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IRepositoryBase<TDao> _repository;
private readonly IMapper _mapper;
public BaseController(IRepositoryWrapper repositoryWrapper, IRepositoryBase<TDao> repository, IMapper mapper)
{
_repositoryWrapper = repositoryWrapper;
_repository = repository;
_mapper = mapper;
}
[HttpGet]
public virtual async Task<ActionResult<PagedResponse<TResponse>>> GetAll([FromQuery] StringQueryRequest request)
{
var paginationFilter = _mapper.Map<PaginationFilter>(request);
var pagedResponse = await _repository.ToPagedList
(_repository.FindAll(request.SearchString), paginationFilter);
var mapPagination = pagedResponse.MapPagination<TResponse, TDao>(_mapper);
return mapPagination.HandleToResponse();
}
[HttpGet("{id:guid}")]
public virtual async Task<ActionResult<Response<TResponse>>> GetById(Guid id)
{
var result = await GetDtoById(id);
return result.HandleGetToResponse();
}
[HttpPost]
public virtual async Task<ActionResult<Response<TResponse>>> Create([FromBody] TCreateRq request)
{
var entity = _mapper.Map<TDao>(request);
_repository.Create(entity);
await _repositoryWrapper.SaveAsync();
var baseResponse = _mapper.Map<TResponse>(entity);
var result = new Result<TResponse>(baseResponse);
return result.HandleToResponse();
}
[HttpPut("{id:guid}")]
public virtual async Task<ActionResult<Response<TResponse>>> UpdateById([FromRoute] Guid id,
[FromBody] TUpdateRq updateRq)
{
var getResult = await GetDtoById(id);
if (getResult.IsNone)
{
return getResult.HandleGetToResponse();
}
var value = await _repository.FindById(id);
//override the current DB objet from the values received from the request
//Not replacing the full object from the requests as some fields may be missing from the update RQ model (eg user password)
var entity = _mapper.Map(updateRq, value);
_repository.Update(entity);
await _repositoryWrapper.SaveAsync();
var baseResponse = _mapper.Map<TResponse>(entity);
var result = new Result<TResponse>(baseResponse);
return result.HandleToResponse();
}
[HttpDelete("{id:guid}")]
public virtual async Task<ActionResult<Response<TResponse>>> DeleteById([FromRoute] Guid id)
{
var getResult = await GetDtoById(id);
if (getResult.IsNone)
{
return getResult.HandleGetToResponse();
}
var baseResponse = getResult.ValueUnsafe();
var entity = _mapper.Map<TDao>(baseResponse);
_repository.Delete(entity);
await _repositoryWrapper.SaveAsync();
var result = new Result<TResponse>(baseResponse);
return result.HandleToResponse();
}
private async Task<Option<TResponse>> GetDtoById(Guid id)
{
var findById = await _repository.FindById(id);
return _mapper.Map<TResponse>(findById);
}
}
I cant figure out a way to implement this and I'd be grateful if someone could give some help.
This is the problem with relying on a purely Generic approach. Generic implies that regardless of what you pass in, they can be treated within the scope of the Generic class as identical. That said, as a base implementation, what you have isn't bad, but you are going to hit some limitations since a Generic implementation represents a "lowest common denominator" in terms of leveraging what EF can bring to the table.
Your issue can be addressed by structuring your controllers around aggregate roots and performing operations through those roots. The trouble you will find when working with related entities is that your controllers will need to know about these relationships in order to ensure required details are eager loaded to work with. You can leverage a Generic base class for controllers and repositories, but the actual controllers and repositories would be scoped around the aggregate root such as the Quotation. Adding quotation items would be done through its root (Quotation) rather than having something like a QuotationItemController and QuotationItemRepository. The goal of the QuotationController would be to support methods that can load a Quotation with it's respective items and have a method like AddQuotationItem. AddQuotationItem would then load the Quotation by ID, eager loading the items, validate the input for a QuotationItem, Create the QuotationItem, append it to the quote.Items, and increment the quote's Amount before saving the changes.
The key here would be to not think of fitting everything into a Generic pattern, but more-so leveraging a Generic if needed for elements of the controller or repositories etc. that are identical.
Some other tips: Which mapper are you using? If it is Automapper then consider using ProjectTo<TDestination>(config) rather than Map<TDestination>() when projecting to the view model. The benefit here is that the projection works across the IQueryable rather than introducing the overhead of fetching Entities and counting on related entities to be eager loaded or waking the performance troll that is lazy loading. This will be problematic with methods like FindAll if that is returning IEnumerable<TEntity> but if it is returning IQueryable<TEntity> then you should be set. Passing FindAll to something like ToPagedList won't save performance hits if FindAll is returning IEnumerable. When converting back from a view model to a DTO, provided your entities are tracked you should avoid using Update and just use Automapper's
mapper.Map(updateRq, value) which will copy the values from source to destination. If Destination is a tracked entity, then just call SaveChanges and EF will build an appropriate UPDATE statement for just the values that changed if any values actually changed. (Update will generate an UPDATE statement for all values even if nothing actually changed.)
That Update pattern is more a case of using:
var dao = mapper.Map<TEntity>(dto);
context.Update(dao);
context.SaveChanges();
which I would never recommend using over:
var dao = context.Entities.Single(x => x.Id == dto.Id);
mapper.Map(dto, dao);
context.SaveChanges();
As the later pattern validates that the dto passed in has a corresponding entity, Automapper should be configured to only copy across allowed properties according to configured rules, then SaveChanges would produce an efficient UPDATE SQL statement only if anything actually changed.

Domain Model and related data (anemic domain model)

I'm currently working with ASP .NET Core 1.0 using Entity Framework Core. I have some complex calculations with data from the database and I'm not sure how to build a proper architecture using Dependency Injection without building an anemic domain model (http://www.martinfowler.com/bliki/AnemicDomainModel.html)
(Simplified) Example:
I have the following entities:
public class Project {
public int Id {get;set;}
public string Name {get;set;}
}
public class TimeEntry
{
public int Id {get;set;}
public DateTime Date {get;set;}
public int DurationMinutes {get;set;}
public int ProjectId {get;set;}
public Project Project {get;set;}
}
public class Employee {
public int Id {get;set;}
public string Name {get;set;}
public List<TimeEntry> TimeEntries {get;set;}
}
I want to do some complex calculations to calculate a monthly TimeSheet. Because I can not access the database within the Employee entity I calculate the TimeSheet in a EmployeeService.
public class EmployeeService {
private DbContext _db;
public EmployeeService(DbContext db) {
_db = db;
}
public List<CalculatedMonth> GetMonthlyTimeSheet(int employeeId) {
var employee = _db.Employee.Include(x=>x.TimeEntry).ThenInclude(x=>x.Project).Single();
var result = new List<CalculatedMonth>();
//complex calculation using TimeEntries etc here
return result;
}
}
If I want to get the TimeSheet I need to inject the EmployeeService and call GetMonthlyTimeSheet.
So - I end up with a lot of GetThis() and GetThat() methods inside my service although this methods would perfectly fit into the Employee class itself.
What I want to achieve is something like:
public class Employee {
public int Id {get;set;}
public string Name {get;set;}
public List<TimeEntry> TimeEntries {get;set;}
public List<CalculatedMonth> GetMonthlyTimeSheet() {
var result = new List<CalculatedMonth>();
//complex calculation using TimeEntries etc here
return result;
}
}
public IActionResult GetTimeSheets(int employeeId) {
var employee = _employeeRepository.Get(employeeId);
return employee.GetTimeSheets();
}
...but for that I need to make sure that the list of TimeEntries is populated from the database (EF Core does not support lazy loading). I do not want to .Include(x=>y) everything on every request because sometimes I just need the employee's name without the timeentries and it would affect the performance of the application.
Can anyone point me in a direction how to architect this properly?
Edit:
One possibility (from the comments of the first answer) would be:
public class Employee {
public int Id {get;set;}
public string Name {get;set;}
public List<TimeEntry> TimeEntries {get;set;}
public List<CalculatedMonth> GetMonthlyTimeSheet() {
if (TimeEntries == null)
throw new PleaseIncludePropertyException(nameof(TimeEntries));
var result = new List<CalculatedMonth>();
//complex calculation using TimeEntries etc here
return result;
}
}
public class EmployeeService {
private DbContext _db;
public EmployeeService(DbContext db) {
_db = db;
}
public Employee GetEmployeeWithoutData(int employeeId) {
return _db.Employee.Single();
}
public Employee GetEmployeeWithData(int employeeId) {
return _db.Employee.Include(x=>x.TimeEntry).ThenInclude(x=>x.Project).Single();
}
}
public IActionResult GetTimeSheets(int employeeId) {
var employee = _employeeService.GetEmployeeWithData(employeeId);
return employee.GetTimeSheets();
}
Do not try to solve querying problems with your aggregates. Your aggregates are meant to process commands and protect invariants. They form a consistency boundary around a set of data.
Is the Employee object responsible for protecting the integrity of an employee's timesheet? If it doesn't then this data doesn't belong into the Employee class.
Lazy-loading may be fine for CRUD models, but is usually considered an anti-pattern when we design aggregates because those should be as small and cohesive as possible.
Are you taking business decisions based on the calculated result from timesheets? Is there any invariants to protect? Does it matter if the decision was made on stale timesheet data? If the answer to these questions is no then your calculation is really nothing more than a query.
Placing queries in service objects is fine. These service objects may even live outside the domain model (e.g. in the application layer), but there is no strict rule to follow. Also, you may choose to load a few aggregates in order to access the required data to process the calculations, but it's usually better to go directly in the database. This allows a better separation between your reads & writes (CQRS).
If I understood your question correctly you can use a trick with injecting a service into your entities that helps it do the job, e.g.:
public class Employee()
{
public object GetTimeSheets(ICalculatorHelper helper)
{
}
}
Then in your service that holds the employees you would obtain it in the constructor and pass to the employee class for calculations. This service can be a Facade e.g. for getting all the data and perform initialization or whatever you really need.
As for the TimeEntries, you can get them using a function like this:
private GetTimeEntries(ICalculationHelper helper)
{
if (_entries == null)
{
_entries = helper.GetTimeEntries();
}
return _entries;
}
It depends of course on you strategy of caching and so on if this pattern fits you.
Personally I find it rather easy to work with anemic classes and have a lot of the business logic in services. I do put some in the objects, like e.g. calculating FullName out of FirstName and LastName. Usually stuff that does not involve other services. Though it's a matter of preference.

Rendering IEnumerable<string> from an entity framework generic repository

I am not sure if this is possible. I have several particular tables in my database represented by entity framework's code first classes. These classes have different properties except in terms of the Id property which is always a string. I am wondering if there is anyway to create a generic repository that can select and render a sequence of just the Id property. For example something like:
class GetDbIds<T> where T : class
{
// PROPERTIES
DbContext DbContext {get;set;}
DbSet<T> DbSet {get;set;}
// CONSTRUCTOR
public GetDbIds(DbContext dbContext)
{
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
// METHODS
public IEnumerable<string> GenerateNewIdSequence()
{
return DbSet.Select(x => x.Id);
}
}
I know how to set up a basic generic repository, but I haven't came across any patterns that also let you dynamically query the repository as well.
You can constrain T to an interface that has the property:
interface IIdentifiable { string Id { get; } }
class GetDbIds<T> where T : IIdentifiable, class

What is a good pattern for a repository and EF data context that return different types?

I have a nice clean domain layer in my app that was developed in a DDD fashion. The database was not considered at all when developing the domain. Property names make sense, aren't in ALL CAPS, and are relevant to my application.
Today, I am implementing a repository to pull from an existing EF DbContext. The DbContext was developed to (basically) match a poorly-designed Oracle database.
Ideally, I would like to implement a repository like this:
public interface IRepository {
IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity;
}
T is my domain entity. But, inside my Find method in my repository, I have to...
Somehow convert the expression to work with the DbContext
I am not sure how to do this yet.
Query the DbContext
Once the expression is 'mapped', this is simple
Somehow map to my domain object
I'm sure I can use AutoMapper or implement my own mapper.
Return an IQueryable having not made a trip to the database yet.
Not sure this is possible after all the meddling done in #'s 1 - 3
So, how has this problem been solved in the past? Are there any reusable patterns here?
Well, you're on the right track already, just implement what your say you want :)
1.You're passing an expression into your find method so, just use that expression in your Where clause
2.You just need to get the correct DbSet from your DbContext to query against, DbContext has a method to get the DbContext of a given type, use that and you can query like
public IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity
{
var dbSet = context.Set<T>();
return dbSet.Where(query);
}
3.If your domain objects are not the ones mapped by EF to the database, you'll need to customize your mapping against what's in your DB in your DbContext class (no need for automapper for that), so you would have something like this in your DbContext class
public class MyContext : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Map(a => a.ToTable("DB_USERS"))
.Property(a => a.Email).HasColumnName("MAIL");
base.OnModelCreating(modelBuilder);
}
}
To map from the table DB_USERS in the DB to the class User, having different names for the fields, etc. here's an article on that
http://www.codeproject.com/Articles/165720/Using-the-Code-First-Model-Configuration-Classes
You could also map the properties to the correct table columns using attributes if you don't want/can't change your DbContext class
http://msdn.microsoft.com/en-us/data/gg193958
Or you can have a different set of entities that are mapped to your DB and use automapper to translate them into your domain objects, but you lose no. 4 bellos since you'll need to materialize the query to automap it to your domain model.
4.No need to do anything special, EF takes care of the that
UPDATE: Solution without having access to the DbContext (not fully generic version but works)
The idea is to create the mapping part of the repository for each domain class, so all gets binded correctly. Continueing with the User domain model and DBUser table model:
public class User : IDomainModelEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class DBUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int USER_ID { get; set; }
[Required]
[MaxLength(150)]
public string USER_NAME { get; set; }
[Required]
[MaxLength(260)]
public string USER_MAIL { get; set; }
}
Then you would have an abstract Repository and an a concrete repository per domain class that implements the basic GetAll query mapped:
public abstract class Repository<T> where T : IDomainModelEntity
{
protected readonly DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
public abstract IQueryable<T> GetAll();
public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
{
return GetAll().Where(predicate);
}
}
public class UserRepository : Repository<User>
{
public UserRepository(DbContext context)
: base(context)
{
}
public override IQueryable<User> GetAll()
{
return _context.Set<DBUser>()
.Select(u => new User
{
Id = u.USER_ID,
Name = u.USER_NAME,
Email = u.USER_MAIL
});
}
}
now to use it you will just call the find or get all on the repository...
using (var context = new CompanyDbContext())
{
var repo = new UserRepository(context);
var list = repo.Find(a=>a.Id >= 2).ToList();
list.ForEach(a => Console.WriteLine("Id: {0}, Name {1}, email {2}", a.Id, a.Name, a.Email));
}
It is not fully generic since you will need to pass a repository for each domain class you need to use, but it may be an acceptable compromise
Hope this helps

SharpArch.Core.PreconditionException: For better clarity and reliability, Entities with an assigned Id must call Save or Update

When I do a save of an entity with assigned id I get a
SharpArch.Core.PreconditionException:
For better clarity and reliability,
Entities with an assigned Id must call
Save or Update
My class is
public class CompanyUserRepository :
RepositoryWithTypedId<CompanyUser, string>, ICompanyUserRepository
{
public override CompanyUser SaveOrUpdate(CompanyUser entity)
{
var user = base.SaveOrUpdate(entity);
//Do some stuff
return user;
}
}
How do I go about saving this entity?
RepositoryWithTypedId does not expose a Save method
Related question. This tells you the reason, but I haven't found out the Sharp Architecture way to do a Save.
Digging through the NorthWind example in the S#arp repo, I found out the answer.
The Repository class should derive from NHibernateRepositoryWithTypedId
e.g.
public class CustomerRepository : NHibernateRepositoryWithTypedId<Customer, string>, ICustomerRepository
{
public List<Customer> FindByCountry(string countryName) {
ICriteria criteria = Session.CreateCriteria(typeof(Customer))
.Add(Expression.Eq("Country", countryName));
return criteria.List<Customer>() as List<Customer>;
}
}

Categories

Resources