Problems with Generic Repository Pattern and Entity Framework - c#

There are a large number of examples on how to use the Generic Repository Pattern over Entitry Framework but none ever go into great detail. More often than not the example they use for accessing data from a webapi is a one to one mapping to the model.
For Eg. Get a single model object _carRepository<Car>.Get(id); or get all cars _carRepository<Car>.GetAll();
The way I have it implemented is in seperate layers like below:
Controller -> Service Layer -> Repository Layer -> DataAccess
I have my service layer using AutoMapper where I map from the model(entity) to a DTO and that gets returned to the controller who returns that.
The issue that I cant get my head around is how do I return an object from the repository layer which isnt of type < T>.
Eg. I want to return some data for a grid lets say a combination of multipe entities like a customers last number of purchases. Am I supposed to pass the Customer, Product, Orders repository into the service and make multiple calls to the database and aggregate? Or I was thinking in the cutomers repository just make a method which returns like a view dto of sorts already aggreating the data where it does a linq query and projects to this dto. The issue I have is that I'm sure my repository shouldnt know about dto's?
Would appreciate anyone who has thoughts on the correct way of doing this?

In short, I don't ever recommend using the Generic Repository (anti)pattern. The trouble is that it ends up being far too restrictive and inefficient. Methods like GetAll() serve only to end up materializing entire tables into memory. They don't accommodate things like projection (essentially what you are looking for) as well as eager loading related data, filtering, sorting, or things like supporting pagination.
The way I recommend looking at repositories is as a one-to-one supplier of data for a Controller in the MVC pattern. If you have a CarController you have a CarRepository to serve all data for that controller. If you break it down to CarController and AddCarController then similarly you can build repositories to serve each of these. These repositories are not Generic in that they don't just serve Car entities, but any and all entities from the DbContext that this controller (or service) needs rather than trying to marry repositories to a specific entity. This gives them only one concern for their existance, so a CarController only needs to worry about primarily one repository, and that repository only has to worry about serving that controller. (Rather than every controller that might want information about a Car)
Regarding using a Service between the controller and repository, I would only suggest this if there is a distinct requirement to provide that separation. For example, if you want to support both an MVC controller and a public facing WebAPI and you want the inputs and outputs provided to these to be 100% consistent. The data transported from Service to Consumer (Controllers, etc.) would be DTOs. This adds the complexity of needing to send details like sorting, pagination, and filtering etc. from the consumer to the service.
Removing the service can make interacting with the repository a lot easier, and you can leverage projection to populate the DTOs/view models actually sent to the view via the controller actions/endpoints. The way I facilitate this is leveraging IQueryable<TEntity> in the repository. This lets the repository handle low level filtering/rules if necessary and lets the consumer (controller) handle determining how it wants to consume the data.
For example if we have a CarController with a CarRepository and CarRepository has a method:
public IQueryable<Car> GetCars(bool includeInactive = false)
{
var query = _context.Cars.AsQueryable();
if (!includeInactive)
query = query.Where(x => x.IsActive);
return query;
}
When it comes time for the controller to request cars from the repository, it has full control over how to consume it:
var cars = await CarRepository.GetCars()
.Where(x => x.Make == make && x.Model == model)
.OrderBy(x => x.ModelYear)
.Select(x => new CarSummary
{
Make = x.Make,
Model = x.Model,
ModelYear = x.ModelYear,
Color = x.Color,
Features = x.Features.Select(...)
}.Skip(pageSize * (pageNumber-1))
.Take(pageSize)
.ToListAsync();
The controller has full control over how the data should come back including whether to run an async query or synchronous one all without adding any complexity to the repository. So here we can project (Select, or leverage Automapper and ProjectTo) to get whatever data we need from the entities and their related data. This leads to building far faster and memory/network efficient queries because the projections only worry about the data the end consumer actually needs. When we want to load an entity and its relations to perform an update, we can fetch the entity and eager load the related data we need to inspect/validate/update. Eager loading data is faster than lazy loading, but both use a fair bit of memory so we don't want to be serializing and transmitting entire object graphs. However, when doing an update we are typically only dealing with a single top-level object at a time.
The question then becomes "Why use a repository then?" The repository provides a nice abstraction for unit testing, and it can help standardize core rules you want in the system such as Active/Inactive (soft delete) and things like authorization in systems using multi-tenancy or otherwise having distinct access controls for the current user as to what data they should ever be able to see. If you don't have either of these requirements, then there isn't really much point to using a repository at all. The DbContext and it's DbSets essentially serve that role.
If you do have plans for multiple consumers, then the repository remains the same but the above consuming code happens in the Service, returning the DTO/ViewModel. This means that your controller will need to package and transmit standardized requests to the service or parameters covering whether it expects any sorting, pagination, etc. Again, I wouldn't recommend taking on that overhead and complexity unless there is a very real requirement justifying it. It just serves to make the code harder to work with.

This may not be complete answer but few things while working with Generic Repository.
Assumption I made that your generic repository return type of T and T is the entity.
Following is not exact interface but based on your question.
public class IRepository<T>
{
T Get(int Id);
IEnumrable<T> GetAll();
}
So most of the scenario above will suffice the thing.
Now let's assume that you have requirement that will only return specific attribute of T or partial of T.
Generic repository implementation never say that you don't need any extra or extended repository.
So you can do like this.
public interface ICarRepository : IRepository<Car>
{
IList<int> GetIds();
IList<string> GetAllModels();
bool IsActiveInProduction(int carId);
}
Implement above just like you have implemented IRepository for EF core. Inject this in your services and Use ICarRepository instead of IRepository
for your development.
Note: This is personal preference but for large project GenericRepository become issue sometime or may not completely fit but Repository is useful pattern and it overall depends on how you use it.
Update 1
In Car Repository if you want to return some other DTO but make sure it related to Car Entity otherwise it is not reponsibility of Repo layer.
public interface ICarRepository : IRepository<Car>
{
IList<int> GetIds();
IList<string> GetAllModels();
bool IsActiveInProduction(int carId);
MyDto GetMyDto(int carId); // MyDto is just for explanation.
}

Below is the BaseRespository.cs code that is use that I use as Generic class for entity framework. I inherit this class as parent with my other RepositoryClass.
public class BaseRepository
{
#region Ctor(s)
protected RuhBotEntities RuhBotEntities;
public BaseRepository(RuhBotEntities entities)
{
this.RuhBotEntities = entities;
}
#endregion
#region Protected & Private Members
protected async Task<T> Get<T>(Guid id) where T : class
{
return await this.RuhBotEntities.Set<T>().FindAsync(id);
}
protected IQueryable<T> GetAll<T>(out int totalCount ,bool isDeleted, string searchField = null, string searchTerm = null,
int? pageIndex = null, int? pageSize = null, string sortBy = null, SortOrder? sortOrder = null) where T : class
{
IQueryable<T> entities = this.RuhBotEntities.Set<T>();
var sortByProperty = typeof(T).GetProperty(sortBy);
if (!string.IsNullOrWhiteSpace(sortBy) && sortOrder.HasValue && sortByProperty != null)
{
switch (sortByProperty.PropertyType.FullName)
{
case "System.Int32":
entities = this.RuhBotEntities.Set<T>().Sort<T, int>(sortBy, sortOrder);
break;
case "System.String":
entities = this.RuhBotEntities.Set<T>().Sort<T, string>(sortBy, sortOrder);
break;
}
}
PropertyInfo[] entityProperties = typeof(T).GetProperties();
var propertyList = entityProperties.Where(p => p.PropertyType.FullName == "System.String").Select(p => p.Name).ToList();
if (!string.IsNullOrWhiteSpace(searchTerm) && !string.IsNullOrWhiteSpace(searchField))
{
entities = entities.Where(string.Format("{0}.Contains(#0)", searchField), searchTerm);
}
else if (!string.IsNullOrWhiteSpace(searchTerm) && propertyList.Count > 0)
{
var searchFieldList = new List<string>();
propertyList.ForEach(p => searchFieldList.Add(string.Format("{0}.Contains(#0)", p)));
var propertySearchField = String.Join("||", searchFieldList);
entities = entities.Where(propertySearchField, searchTerm);
}
if (isDeleted)
{
entities = entities.Where(string.Format("{0}.Equals(#0)", "IsDeleted"), false);
}
totalCount = entities.Count();
if (pageIndex.HasValue && pageSize.HasValue)
{
entities = entities.OrderBy(sortBy).Skip(pageIndex.Value * pageSize.Value).Take(pageSize.Value);
}
return entities;
}
protected void Add<T>(T entity) where T : class
{
this.RuhBotEntities.Set<T>().Add(entity);
}
public async Task<T> AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var dbSet = this.RuhBotEntities.Set<T>();
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return exists ? await dbSet.FirstOrDefaultAsync(predicate) : dbSet.Add(entity);
}
protected void Update<T>(T entity, params string[] fieldsExcludedForUpdation) where T : class
{
var dbEntityEntry = this.AttachEntity(entity);
var createdOn = dbEntityEntry.Entity.GetType().GetProperty("CreatedOn");
var createdBy = dbEntityEntry.Entity.GetType().GetProperty("CreatedBy");
if (createdOn != null)
{
dbEntityEntry.Property("CreatedOn").IsModified = false;
}
if (createdBy != null)
{
dbEntityEntry.Property("CreatedBy").IsModified = false;
}
if (fieldsExcludedForUpdation != null && fieldsExcludedForUpdation.Length > 0)
{
foreach (var field in fieldsExcludedForUpdation)
{
dbEntityEntry.Property(field).IsModified = false;
}
}
dbEntityEntry.State = EntityState.Modified;
}
protected void Delete<T>(Guid id) where T : class
{
var entity = this.RuhBotEntities.Set<T>().Find(id);
this.RuhBotEntities.Set<T>().Remove(entity);
}
protected async Task SaveChanges()
{
await this.RuhBotEntities.SaveChangesAsync();
}
private DbEntityEntry AttachEntity(object entity)
{
var entry = this.RuhBotEntities.Entry(entity);
if (entry.State == EntityState.Detached)
{
var set = this.RuhBotEntities.Set(entity.GetType());
object attachedEntity = set.Find(entity.GetType().GetProperty("Id").GetValue(entity));
if (attachedEntity != null)
{
var attachedEntry = this.RuhBotEntities.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
return attachedEntry;
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
this.RuhBotEntities.Set(entity.GetType()).Attach(entity);
this.RuhBotEntities.Entry(entity).State = EntityState.Modified;
}
return entry;
}
#endregion
}

Related

Projecting entities from one dbcontext to another best practices

I have two databasecontexts FooContext, BarContext.
For FooContext, when certain entities in the dbSet changes, I need to project these entities to related entities in BarContext. I am looking at the best ways to implement such a job.
My current attempt involves inheriting an interface for the relevant entities, filtering these out in overridden FooContext.SaveChangesAsync() before calling a separate mapper (using dependencyInjection) to update the relevant tables in BarContext
// Baz.cs
public class Baz : IProjectedEntity {
// baz-definition
}
// FooContext.cs
public class FooContext : DbContext {
private IJobSpawner _jobSpawner;
public FooContext(IJobSpawner jobSpawner){
_jobSpawner=jobSpawner;
}
public DbSet<Baz> Baz {get;}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new()) {
ChangeTracker.DetectChanges();
var projections = ChangeTracker
.Entries<IProjectedEntity>()
.Where(e => e.State != EntityState.Unchanged)
.Select(e => e.Entity).ToList();
int changes = await base.SaveChangesAsync(cancellationToken);
await _jobSpawner.ProjectEntities(projections);
return changes;
}
}
Then the implementation of IJobSpawner takes dependency on BarContext and does the necessary mappings.
Is this a bad idea?
What is an alternative way of doing this?
I have also played a bith with the thought of defining an attribute for class properties, such that projections only happen if certain properties is changed
note : The method i have proposed does work, but I feel there should be a better, less cluttered way of achieving the same result. I don't like having to much internal logic in the context

Modifying existing generic repository pattern to add select

I want to modify existing generic repository to add optional select functionality like in Entity Framework Core.
Desired result:
private readonly IUnitOfWork _unit;
// ...
// without using select functionality
IEnumerable<Entity> entities = await _unit.Account.AllAsync();
Entity? x = await _unit.Account.SingleAsync(x => x == id);
// using select functionality
IEnumerable<DTO> y = await _unit.Account.AllAsync(select: x => new DTO
{
Name = x.Name
});
DTO? y = await _unit.Account.SingleAsync(x => x == id, select: x => new DTO
{
Name = x.Name
});
I tried to implement solution from this question Select specific columns in a generic repository function but parameter was required and I want it to be optional.
For simplicity I only leave methods to which I want to add this functionality in generic repository:
in IBaseRepository.cs
public interface IBaseRepository<T> where T : BaseEntity
{
Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0,
int take = int.MaxValue,
Track track = Track.NoTracking);
Task<T?> SingleAsync(
Expression<Func<T, bool>> filter, Func<IQueryable<T>,
IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking);
}
in BaseRepository.cs
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly DataContext _context;
internal DbSet<T> _set;
public BaseRepository(DataContext context)
{
_context = context;
_set = context.Set<T>();
}
public async Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0, int take = int.MaxValue, Track track = Track.NoTracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = skip == 0 ? query.Take(take) : query.Skip(skip).Take(take);
query = filter is null ? query : query.Where(filter);
query = order is null ? query : order(query);
query = include is null ? query : include(query);
return await query.ToListAsync();
}
public async Task<T?> SingleAsync(
Expression<Func<T, bool>> filter,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = filter is null ? query : query.Where(filter);
query = include is null ? query : include(query);
return await query.SingleOrDefaultAsync();
}
}
in BaseEntity.cs (every class-dbtable would inherit from BaseEntity)
public abstract class BaseEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdatedAt { get; set; }
}
Ask yourself honestly: "Why do I believe I need a Generic Repository?"
A: "Because I want to add a layer of abstraction over EF so I can replace it if needed."
You are a victim of a self-fulfilling prophecy; Handcuffing yourself and your application's capability to leverage EF to it's fullest. The solution will either be so limited or complex and problematic that EF will be considered too slow or incapable of doing what is needed and needs to be replaced with something someone convinces you is better/faster.
A: "Because I don't want to pollute my business logic with domain knowledge or EF-specific knowledge."
This is a lie. Passing expressions into your repository pollutes the domain with domain and EF-specific knowledge & limitations. If the expressions are passed to EF's Linq methods, they must conform to the domain and they must be palatable to EF. You remove the reference to EF, but still impose the EF's limitations on your consumer. You cannot have references to methods in the expressions, or use unmapped properties. EF and your chosen provider need to be able to translate these expressions down to SQL, otherwise you are left writing even more code to introduce your own expression parser and adapter.
A: "Because I want to be able to introduce a point of abstraction so I can more easily write unit tests."
This is a good reason, but it can be solved in a considerably more simple approach:
.
IQueryable<T> All();
IQueryable<T> Single(int id);
That's it. Your consumer can support filtering, sorting, pagination, exists check, count, projection, and even determine for itself whether the overhead of an async operation is necessary. It's dead simple to mock out, adding just a pinch of complexity to support passing back data for async consumption. The repository method can impose base-level filtering such as IsActive for soft-delete systems, or checking authorization for the current user and factoring that into the results returned.
Where you want to introduce a solution to satisfy "DNRY" (Do not repeat yourself) or enforce a level of conformity in your application, or between identical (not merely similar) concerns such as a Web API service and a web application,
that can be done by handing off to a common service that fetches and packages data in a consistent way using the repository to return DTOs. Otherwise treat the repository like you would an MVC Controller, scope it to be responsible for one consumer, and only have that one reason to change.
The typical argument against returning IQueryable is that it is a weak or leaky abstraction and hands the consumer a loaded shotgun. This is 100% true that it is a weak abstraction, this makes it very easy to mock out. If you don't need to mock it out for unit tests, You just "ain't gonna need it." As far as giving developers a loaded weapon, IMHO it is far, far better for a project to give implementation developers the tools they need to be able to write efficient expressions against the domain and the training/knowledge how to do it properly, and correct mistakes if and when they are found; than it is to try and abstract the technology away to "dumb it down" or in essence, end up introducing just as much complexity and ability to make a mess, just with your name across the interface rather than EF.

Whats the best practice of linq ASP.NET MVC respository pattern

I'm a junior web developer trying to learn more every day.
What it the best practice for you guys to performe MVC repository pattern with Linq?
The one I use:
Create extra clases with the exact name of my .tt files with CRUD method like getAll(), getOne(), Update(), Delete() filling my own class with the entity framework and returning this, or using the entity framework crude
this is an example of what I'm actually doing.
this is my getAll method of my class for example User
public class CEmployee : CResult
{
public string name{get;set;}
public string lastname{get;set;}
public string address{get;set;}
//Extracode
public string Fullname // this code is not in the .tt or database
{
get
{
return name + lastname;
}
}
public <List>CEmployee getAll()
{
try
{
var result = (from n in db.Employee
select new CEmployee // this is my own class I fill it using the entity
{
name = n.name,
lastname = n.lastname,
address = n.address
}).ToList();
if (result.Count > 0)
{
return result;
}
else
{
return new List<CResult>
{
new CResult
{
has_Error = true,
msg_Error = "Element not found!!!!"
}
}
}
}
catch
{
return Exception();
}
}
}
that the way I do all thing I return a filled of my type, but on the web I see that people return the entity type normaly, But I do this to manipulate my response, And if I want to return extra information I just have to neste a list for example, whats the best way guys, return mytype or return the entity type ?
PD, I also use this class like my ViewModel.And I do this for all my classes.
One of the projects I am currently one uses Dependency Injection to setup the DAL (Data Access Layer.) We also are using an n-Tier approach; this separates the concern of the repository from the Business Logic and Front End.
So we would start with 4 or so base projects in the application that link to each other. One of that handles the Data Access, this would be your repository; read up on Ninject for more info on this. Our next tier is our Domain which houses the Entities built by the t4 template(.tt files) and also our DTO's (data transfer objects which are flat objects for moving data between layers.) Then we have a service layer, the service layer or business logic layer holds service objects that handle CRUD operations and any data manipulation needed. Lastly we have our front end which is the Model-View-ViewModel layer and handles the controllers and page building.
The MVVM calls the services, the service objects call the data access layer and Entity Framework works with Ninject to access the data and its stored in the DTO's as it is moved across layers.
Now this may seem overly complex depending on the application you are writing, this is built for a highly scalable and expandable web application.
I would highly recommend going with a generic repository implementation. The layers between your repository and the controller vary depending on a number of factors (which is kind of a broader/bigger topic) but the generic repository gets you going on a good implementation that is lightweight. Check out this article for a good description of the approach:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Ideally in a MVC application, you will want to repositories in a different layer like in a separate project, let's call it Data layer.
You will have an IRepository interface that contain generic method signatures like GetAll, GetById, Create or UpdateById. You will also have abstract RepositoryBase class that contain shared implementation such as Add, Update, Delete, GetById, etc.
The reason that you use an IRepository Interface is, there are contracts for which your inherited repository class, such as EmployeeRepository in your case, need to provide concrete implementations. The abstract class serves as a common place for your shared implementation (and override them as you need to).
So in your case, what you are doing using LINQ with your DbContext is basically correct, but implementation like your GetAll method should be part of the generic/shared implementation in your abstract class RepositoryBase:
public abstract class RepositoryBase<T> where T : class
{
private YourEntities dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected YourEntities DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbset.ToList();
}
}
I would suggest you need to think about whether or not to return an error result object like CResult, and think about if your CEmployee and CResult should exist in this parent-child relationship. Also think about what you want to do with your CResult Class. It seems to me your CEmployee handles too many tasks in this case.

return Queryable<T> or List<T> in a Repository<T>

Currently I'm building an windows application using sqlite. In the data base there is a table say User, and in my code there is a Repository<User> and a UserManager. I think it's a very common design. In the repository there is a List method:
//Repository<User> class
public List<User> List(where, orderby, topN parameters and etc)
{
//query and return
}
This brings a problem, if I want to do something complex in UserManager.cs:
//UserManager.cs
public List<User> ListUsersWithBankAccounts()
{
var userRep = new UserRepository();
var bankRep = new BankAccountRepository();
var result = //do something complex, say "I want the users live in NY
//and have at least two bank accounts in the system
}
You can see, returning List<User> brings performance issue, becuase the query is executed earlier than expected. Now I need to change it to something like a IQueryable<T>:
//Repository<User> class
public TableQuery<User> List(where, orderby, topN parameters and etc)
{
//query and return
}
TableQuery<T> is part of the sqlite driver, which is almost equals to IQueryable<T> in EF, which provides a query and won't execute it immediately. But now the problem is: in UserManager.cs, it doesn't know what is a TableQuery<T>, I need to add new reference and import namespaces like using SQLite.Query in the business layer project. It really brings bad code feeling. Why should my business layer know the details of the database? why should the business layer know what's SQLite? What's the correct design then?
I'd recommend you to use IEnumerable<T> rather than IQueryable<T>, which allows lazy loading too. IEnumerable does not, however, imply you can query the data in any way. Your DB LINQ Provider will probably have a reduced feature set.
Typically, in a clean architecture, data query logic is encapsulated in repositories. Use of Pipes and Filters can help reuse query logic. Wrapping these with methods in data-layer/repositories will be more readable and maintainable plus re-usable.
For instance, Pipes and filters to query users:
/// Pipes/Filters for user queries.
public static class UserExtensions
{
public static IQueryable<User> Active(this IQueryable<User> query)
{
return query.Where(user => user.Active == true);
}
}
public class UserRepository : IRepository<User>, IUserRepository
{
/// Retrieve all users
public List<User> List()
{
// Logic to query all users in the database.
}
public List<User> ListActive()
{
// Logic to query all active users in the database.
return context.Users.Active().ToList();
}
}
Complex queries requires the understanding of it's purpose and responsibilities to abstract the query logic to it's repositories. For instance, 'Get all accounts belongs to this user" can be written in AccountRepository class as List<Account> ListForUser(int userId) { }.
Edit: Based on the comments, here is the scenarios to write a search query that retrieves Users lives in LA who have at least 2 accounts.
public class UserRepository : IRepository<User>, IUserRepository
{
// other queries.
public List<User> List(ISearchQuery query)
{
// Logic to query all active users in the database.
return context.Users.Active().LivesIn(query.Country).WithAccounts(query.AccountsAtLeast).ToList();
}
}
public static class UserExtensions
{
// other pipes and filters.
public static IQueryable<User> LivesIn(this IQueryable<User> query, string country)
{
return query.Where(user => user.Country.Name == country);
}
public static IQueryable<User> WithAccounts(this IQueryable<User> query, int count)
{
return query.Where(user => user.Accounts.Count() >= count);
}
}
Since TableQuery<T> implements IEnumerable<T>, and not IQueryable<T>, the best solution would be to simply change your repository interface to return IEnumerable<T> instead of TableQuery<T>. This not only breaks the explicit client dependency with your SqlLite library, but it is also a better design to use an abstraction (IEnumerable<T>) instead of an implementation (TableQuery<T>) in your interfaces.
Your example method should look like this:
//Repository<User> class
public IEnumerable<User> List(where, orderby, topN parameters and etc)
{
//query and return
}
Lazy loading can be a PITA and I'd rather try to inject a loading strategy into the repository instead of trying to fiddle with the query object in my application or business layer. Especially when the query object forces me to tightly couple the layers.

How do I keep the Advantages of IQueryable<T> while using ViewModels or DTOs instead of ORM generated Entities?

When using Linq-To-SQL or Entity Framework, the DataContext and generated entities provide IQueryable interfaces for deferred execution. It lets me write code like this:
public class RPO
{
DataContext dc;
public RPO(){ dc = new DataContext(); }
public IQueryable<Data> ReadData()
{
return dc.Data;
}
}
public class Svc
{
RPO repository;
public Svc() { repository = new RPO(): }
public IQueryable<Data> ReadActiveData()
{
return repository.ReadData().Where(d => d.IsActive.Equals(true));
}
public IQueryable<Data> ReadArchiveData()
{
return repository.ReadData().Where(d => d.IsArchived.Equals(true));
}
}
This model falls appart if in the class Svc I return DataModel instead of Data -- how can I keep IQueryable<T> as far down the chain as possible?
Your SVC layer should never expose IQueryable. What happens then is, it is actually your service consumer who gets to execute your query which is a bad pattern. So service should always expose
data which is sufficient for the service-user to work ( display) with.
Preferably a IList or a IEnumarable.
You don't, unless your DTO or ViewModel also implements IQueryable. Once you transform the results into something that isn't IQueryable, that's it.
I know it's considered a bad pattern, but if you need what IQueryable does in that layer of your code, then you should consider just passing that instead of transforming it before that layer.

Categories