Suppose I have the following aggregate root:
public class Aggregate
{
public int Id {get; set;}
public List<Entity> Entities {get; set;}
}
And the following repository:
public class AggregateRepository
{
public Aggregate GetPaged(int Id)
{
return db.Aggregate
.Include(x=>x.Entities)
.Find(id)
}
}
Question: how can I get a paged and sort list of entities? Which is the best approach to get the entities paged and sorted, but also with the aggregate information?
Edited:
What are you think about the following approach?
public class AggregateRepository
{
public IEnumerable<Entity> GetEntitiesPaged(int id)
{
return db.Aggregate
.Include(x=>x.Aggregate)
.Where(x=>x.Id = id)
.Select(x=>x.Entities)
.Take(20);
}
}
Instead of return an aggregate object, I can receive a list of entities (20 entities, in this case) with aggregate object included. Is it a good approach working with an aggregate in DDD pattern?
The short answer is that you should avoid querying your domain model.
Rather use a specialized query layer with a read model if required; else something more raw such as DataRow.
Update:
You should try not to create aggregates when querying. This means not accessing a repository. A query layer would look something like this:
public interface ISomethingQuery
{
IEnumerable<SomethingDto> GetPage(SearchSPecification specification, int pageNumber);
// -or-
IEnumerable<DataRow> GetPage(SearchSPecification specification, int pageNumber);
}
You would then use an implementation of this query interface to get the required data for display/reporting.
First of all you should separate your write-side(commands) from the read side (queries), which called CQRS. You can take a look this example.
But if you just want to get a paged and sorted list of entities, you can use the following approach.
public ICollection<Aggregate> GetSortedAggregates(AggregateListFilter filter, out int rowCount)
{
var query = (base.Repository.CurrentSession() as ISession).QueryOver<Aggregate>();
query = query.And(q => q.Status != StatusType.Deleted);
if (!string.IsNullOrWhiteSpace(filter.Name))
query = query.And(q => q.Name == filter.Name);
rowCount = query.RowCount();
switch (filter.OrderColumnName)
{
case ".Name":
query = filter.OrderDirection == OrderByDirections.Ascending ? query.OrderBy(x => x.Name).Asc : query.OrderBy(x => x.Name).Desc;
break;
default:
query = filter.OrderDirection == OrderByDirections.Ascending ? query.OrderBy(x => x.Id).Asc : query.OrderBy(x => x.Id).Desc;
break;
}
if (filter.CurrentPageIndex > 0)
{
return query
.Skip((filter.CurrentPageIndex - 1) * filter.PageSize)
.Take(filter.PageSize)
.List();
}
return query.List();
}
Related
I have a Provider class and an Article one. Article has a int ProviderId{get;set} and a public virtual Provider Provider {get;set;} properties.
I know about Lazy loading and why I can't access the Provider property in Article outside the context but I have a generic method that returns the next T like this:
public static T Next<T>(T currentElement) where T : class, IModel {
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities()) {
data = context.Set<T>().Single(el => el.Id == currentElement.Id + 1);
}
return data;
}
But I can't retrieve Child entities like Provider in Articles Class. How can I include all entities? Should I update the method and make one per entity?
I read about Eager loading and Explicit loading but I don't know how can I implement these in my method.
Note:
Not all my entities have entity children and I have more methods like Previous<T>(), First<T>() or Last<T>() that do the work you expect.
You could create an overload of your Next method that accepts an Expression<Func<T, object>>:
public static T Next<T>(T currentElement, Expression<Func<T, object>> navigationProperty) where T : class, IModel
{
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
IQueryable<T> dbQuery = context.Set<T>();
if (navigationProperty != null)
dbQuery = dbQuery.Include<T, object>(navigationProperty);
data = dbQuery.AsNoTracking().Single(el => el.Id == currentElement.Id + 1);
}
return data;
}
Usage:
var entity = Next(instance, x => x.Provider);
Please refer to the following blog post for more information and a complete example.
Implementing a generic data access layer using Entity Framework: https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/
You could extend your method to take a list of possible includes. For example:
public static T Next<T>(T currentElement, params Expression<Func<T, object>>[] includes)
where T : class, IModel
{
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
IQueryable<T> query = context.Set<T>();
//Deferred execution allows us to build up the query
foreach (var include in includes)
{
query = query.Include(include);
}
return query.Single(el => el.Id == currentElement.Id + 1);
}
}
And use it like this:
var someEntity = ...;
var nextEntity = Next(someEntity,
e => e.Childcollection1,
e => e.Childcollection2,
//
e => e.ChildcollectionN)
As an additional point, to get your next entity, you shouldn't rely on the ID values being sequential, for example, they will get out of sequence if entries are deleted. Instead, consider something like this:
return query.OrderBy(el => el.Id).First(el => el.Id > currentElement.Id);
So you have a one-to-many relation between Provider and Article: Every Provider has zero or more Articles, and every Article belongs to exactly one Provider.
If you have configured this one-to-many relationship correctly in Entity Framework, a Provider should have a reference to its many Articles:
public virtual ICollection<Article> Articles{get; set;}
Furthermore you have a function Next, that takes as input an object of class T, which is a class that implements IModel. Next returns the next element from the DbSet of Ts, assuming you have a proper definition for the next element.
Apparently you want to adapt function Next such that you can use this function to get the next Provider with all its Articles.
More generic: if T is a type that has properly designed one-to-many relationships in it, and you have an object of type T, you want the T with the first Id higher than the Id of the current T, inclusive all its ICollections.
I've divided this into sever smaller functions:
A function that, given a type T returns all properties that implement ICollection
A function that, given a DbSet returns all elements of the DbSet inclusive all its ICollections
Given those two, and functions like OrderBy and FirstOrDefault you will be able to get the first Provider with Id larger than current provider with all its Articles.
static class QueryableExtensions
{
// returns true if PropertyInfo implements ICollection<...>
public static bool ImplementsICollection(this PropertyInfo propertyInfo)
{
return propertyInfo.IsGenericType
&& propertyInfo.GetGenericTypeDefinition() == typeof(ICollection<>);
}
// returns all readable & writable PropertyInfos of type T that implement ICollection<...>
public static IEnumerable<PropertyInfo> CollectionProperties<T>()
{
return typeof(T).GetProperties()
.Where(prop => prop.CanRead
&& prop.CanWrite
&& prop.PropertyType.ImplementICollection();
}
// given an IQueryable<T>, adds the Include of all ICollection<...> T has
public static IQueryable<T> IncludeICollection<T>(IQueryable<T> query)
{
var iCollectionProperties = CollectionProperties<T>();
foreach (var collectionProperty in collectionProperties)
{
query = query.Include(prop.Name);
}
return query;
}
// given a IQueryable<T> and a T return the first T in the query with Id higher than T
// for this we need to be sure every T has an IComparable property Id
// so T should implement interface IId (see below)
public T Next<T>(this IQueryable<T> query, T currentItem)
where T : IId // makes sure every T has aproperty Id
{
T nextElement = query
// keep only those DbSet items that have larger Ids:
.Where(item => currentItem.Id < item.Id)
// sort in ascending Id:
.OrderBy(item => item.Id
// keep only the first element of this sorted collection:
.FirstOrDefault();
return T
}
}
I need to be sure that every T has an Id, otherwise you can't get the next Id. Probably you have this in your IModel interface:
public Interface IId
{
public int Id {get;}
}
After these functions your query will be like:
public static T Next<T>(T currentElement) where T : class, IModel, IId
{
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
return context.Set<T>().Next(currentElement);
}
}
I'm not looking to rehash an answer, but here is a function that combines a little bit of reflection to automagically load all foreign key related data based on model declarations. I've written it as a way to load an object by something other than its primary key, but it can be modified to do so if needed.
private IQueryable<T> GetFullSet()
{
IEnumerable<IEntityType> entities = _dbContext.Model.GetEntityTypes();
IEntityType thisEntity = entities.SingleOrDefault(x => x.ClrType.Name == typeof(T).Name);
IQueryable<T> set = _dbContext.Set<T>();
foreach (IForeignKey fKey in thisEntity.GetReferencingForeignKeys())
{
set = set.Include(fKey.PrincipalToDependent.Name);
}
return set;
}
I have recently moved from coding in Java to c# and I am still learning the various elements of c#.
To access an existing database, which I cannot redesign, I am using Entity Frameworks 6 and 'Code First from database' to generate contexts and types representing the database tables. I am using Ling-To-SQL to retrieve the data from the database which is heavily denormalized.
My current task is create a report where each section is read from various tables, which all have a relationship to one base table.
This is my working example:
using(var db = new PaymentContext())
{
var out = from pay in db.Payment
join typ in db.Type on new { pay.ID, pay.TypeID } equals
new { typ.ID, typ.TypeID }
join base in db.BaseTable on
new { pay.Key1, pay.Key2, pay.Key3, pay.Key4, pay.Key5 } equals
new { base.Key1, base.Key2, base.Key3, base.Key4, base.Key5 }
where
base.Cancelled.Equals("0") &&
base.TimeStamp.CompareTo(startTime) > 0 &&
base.TimeStamp.CompareTo(endTime) < 1 &&
.
(other conditions)
.
group new { pay, typ } by new { typ.PaymentType } into grp
select new
{
name = grp.Key,
count = grp.Count(),
total = grp.Sum(x => x.pay.Amount)
};
}
There will be a large number of sections in the report and each section will generate a where clause which will contain the conditions shown. In some sections, the required data will be extracted from tables up to five levels below the BaseTable.
What I want to do is create a resuable where clause for each report section, to avoid a lot of duplicated code.
After a lot of searching, I tried to use the solution suggested here , but this has been superseded in Entity Framework 6.
How do I avoid duplicating code unnecessarily?
I did try to use the extension clauses you suggested, but my generated classes do not extend the BaseTable, so I had to explicitly define the link through the navigation property. As only a small number of tables will be common in the queries, I decided to apply the filters directly to each table as required. I will define these as required.
krillgar suggested moving to straight LINQ syntax, which seems like good advice. We intend to redesign our database in the near future and this will remove some of the SQL dependency. I merged the suggested filters and full LINQ syntax to access my data.
// A class to hold all the possible conditions applied for the report
// Can be applied at various levels within the select
public class WhereConditions
{
public string CancelledFlag { get; set; } = "0"; // <= default setting
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
// Class to define all the filters to be applied to any level of table
public static class QueryExtensions
{
public static IQueryable<BaseTable> ApplyCancellationFilter(this IQueryable<BaseTable> base, WhereConditions clause)
{
return base.Where(bse => bse.CancelFlag.Equals(clause.CancelledFlag));
}
public static IQueryable<BaseTable> ApplyTimeFilter(this IQueryable<BaseTable> base, WhereConditions clause)
{
return base.Where(bse => bse.TimeStamp.CompareTo(clause.StartTime) > 0 &&
bse.TimeStamp.CompareTo(clause.EndTime) < 1);
}
}
And the query is composed as follows:
using (var db = new PaymentContext())
{
IEnumerable<BaseTable> filter = db.BaseTable.ApplyCancellationFilter(clause).ApplyTimeFilter(clause);
var result = db.Payment.
Join(
filter,
pay => new { pay.Key1, pay.Key2, pay.Key3, pay.Key4, pay.Key5 },
bse => new { bse.Key1, bse.Key2, bse.Key3, bse.Key4, bse.Key5 },
(pay, bse) => new { Payment = pay, BaseTable = bse }).
Join(
db.Type,
pay => new { pay.Payment.TypeKey1, pay.Payment.TypeKey2 },
typ => new { typ.TypeKey1, typ.TypeKey2 },
(pay, typ) => new { name = typ.Description, amount = pay.Amount }).
GroupBy(x => x.name).
Select(y => new { name = y.Key,
count = y.Count(),
amount = y.Sum(z => z.amount)});
}
And then to finally execute composed query.
var reportDetail = result.ToArray(); // <= Access database here
As this query is the simplest I will have to apply, future queries will become much more complicated.
The nice thing about LINQ is that methods like Where() return an IEnumerable<T> that you can feed into the next method.
You could refactor the where clauses into extension methods akin to:
public static class PaymentQueryExtensions {
public static IQueryable<T> ApplyNotCancelledFilter(
this IQueryable<T> payments)
where T : BaseTable {
// no explicit 'join' needed to access properties of base class in EF Model
return payments.Where(p => p.Cancelled.Equals("0"));
}
public static IQueryable<T> ApplyTimeFilter(
this IQueryable<T> payments, DateTime startTime, DateTime endTime)
where T: BaseTable {
return payments.Where(p => p.TimeStamp.CompareTo(startTime) > 0
&& p.TimeStamp.CompareTo(endTime) < 1);
}
public static IGrouping<Typ, T> GroupByType(
this IQueryable<T> payments)
where T: BaseTable {
// assuming the relationship Payment -> Typ has been set up with a backlink property Payment.Typ
// e.g. for EF fluent API:
// ModelBuilder.Entity<Typ>().HasMany(t => t.Payment).WithRequired(p => p.Typ);
return payments.GroupBy(p => p.Typ);
}
}
And then compose your queries using these building blocks:
IEnumerable<Payment> payments = db.Payment
.ApplyNotCancelledFilter()
.ApplyTimeFilter(startTime, endTime);
if (renderSectionOne) {
payments = payments.ApplySectionOneFilter();
}
var paymentsByType = payments.GroupByType();
var result = paymentsByType.Select(new
{
name = grp.Key,
count = grp.Count(),
total = grp.Sum(x => x.pay.Amount)
}
);
Now that you have composed the query, execute it by enumerating. No DB access has happened until now.
var output = result.ToArray(); // <- DB access happens here
Edit After the suggestion of Ivan, I looked at our codebase. As he mentioned, the Extension methods should work on IQueryable instead of IEnumerable. Just take care that you only use expressions that can be translated to SQL, i.e. do not call any custom code like an overriden ToString() method.
Edit 2 If Payment and other model classes inherit BaseTable, the filter methods can be written as generic methods that accept any child type of BaseTable. Also added example for grouping method.
Assuming that AccountSet returns an IQueryable<Account>, how can I convert this early binding:
XrmServiceContext _xrmServiceContext;
var result = _xrmServiceContext.AccountSet.FirstOrDefault(x => x.Id == locationGuid);
Into something that is more reusable like:
_xrmServiceContext.Where(Set == "Account").FirstOrDefault(x => x.Id == locationGuid);
AccountSet is defined as:
public System.Linq.IQueryable<Xrm.Account> AccountSet
{
get
{
return this.CreateQuery<Xrm.Account>();
}
}
How do I generalize this to be reusable for any IQueryable member of XrmServiceContext ?
As long as the Id is the primary key you can use that in conjunction with find to make a generic call
public T Get<T>(object[] Id)
where T : class
{
return _xrmServiceContext.Set<T>().Find(Id )
}
if your looking for an IQueryable that may be a bit harder but you can find out how to do that off of a previous question I asked:
Entity Framework Filter By PrimaryKey
Courtesy of this post, I've found the following solution:
private static T EntityGet<T>(Guid id)
where T : Entity
{
T item =
(
from query in Context.CreateQuery<T>()
where query.Id == id
select query
).Single();
return item;
}
What is the right way to use repository pattern (with entity framework) when working with multiple set of entities?
Should I create a repository for every entity?
e.g.:
Having following entities: Articles, categories and comments.
Should i have a repository for each one?
I was using repository like this:
public class BaseArticleRepository : BaseRepository
{
private ContentModel _contentctx;
public ContentModel Contentctx
{
get
{
if ((_contentctx == null))
{
_contentctx = new ContentModel();
}
return _contentctx;
}
set { _contentctx = value; }
}
// IDisposable Support code comes here....
}
And sample repository for Articles:
public class ArticlesRepository : BaseArticleRepository
{
public Article GetArticleById(int id)
{
var article = Contentctx.Articles.Where(o => o.ArticleID == id).FirstOrDefault();
return article;
}
public List<Article> GetArticles()
{
var articles = Contentctx.Articles.ToList();
return articles;
}
public List<ArticleHeader> GetArticlesHeaders()
{
var articles = (from article in Contentctx.Articles
select new ArticleHeader
{
ArticleID = article.ArticleID,
Title = article.Title,
CategoryTitle = article.Articles_Categories.Title,
AddedBy = article.AddedBy,
AddedDate = article.AddedDate,
ViewCount = article.ViewCount
}).ToList();
return articles;
}
public List<ArticleHeader> GetArticlesHeaders(int PageIndex, int PageSize)
{
var articles = (from article in Contentctx.Articles
select new ArticleHeader
{
ArticleID = article.ArticleID,
Title = article.Title,
CategoryTitle = article.Articles_Categories.Title,
AddedBy = article.AddedBy,
AddedDate = article.AddedDate,
ViewCount = article.ViewCount
}).OrderBy(p => p.AddedDate).Skip(PageSize * PageIndex).Take(PageSize).ToList();
return articles;
}
public int GetArticleCount(string txtFilter)
{
int ret = Contentctx.Articles.Where(o => o.Title.Contains(txtFilter)).Count();
return ret;
}
public int AddArticle(Article article, int categoryId)
{
Contentctx.AddToArticles(article);
}
}
Basically every repository implements all CRUD data (including getting data with filters and sorts), though I read in some blogs that this is wrong repository pattern implementation because Repository must implement only basic,generic, function to retrieve and insert (remove modify) data.
All sorting, filtering must be done locally in memory.
But I preform to what ever i can on server side (sqlserver).
Why should I load all articles (with all fields) from database if I need only title and abstract?
I would suggest creating a repository for each aggregate root you are dealing with. An aggregate root being the data structure that you actually want to have as the object you are manipulating, i.e. Customer, it may have Address, Orders, Invoices, etc as substructures (the actual customer you retrieve with those related substructures an aggregate of the various tables).
To say "This is the right way" is always a risky affirmation, but I think you should create aggregates (Entities that are composed by various tables) and once it have been done, try to learn about the Unit Of Work (UoW) pattern. The UoW is the way that I use to deal with multiple repositories.
BTW, I'm agree with zespri. Sort and filter in memory is not a good idea.
I have a problem in architecting my application.
I have the following structure with only important aspects shown.
namespace Domain
{
public class Invoice
{
//properties
}
public class InvoiceRepository
{
public Linq2SqlContext context = new Linq2SqlContext();
public IQueryable<Invoice> GetInvoices()
{
var query = from inv in _dbctx.Invoices orderby inv.invInvoiceDate descending select GetInvoice(inv) ;
return query;
}
}
public class InvoiceService()
{
public InvoiceRepository _repository = new InvoiceRepositroy();
public IQueryable<Invoice> GetInvoices()
{
return _repository.GetInvoices();
}
}
}
namespace MyApp
{
public class UI
{
public InvoiceService _service = new InvoiceService();
public void FilterInvoices()
{
var query =
(
from i in _service.GetInvoices()
from s in _service.GetStatuses()
where i.ProjectID == _projectid &&
s.ID == i.Status
select new
{
InvoiceID = i.ID,
DocumentTotal = i.TotalDue.ToString(),
Created = i.Created,
WeekEnding = i.WeekEnding,
Status = s.staStatus
}
).Skip(_pageIndex * _pageSize).Take(_pageSize);
}
}
{
So I want to return IQueryable from my service so I can
filter from client code. But the problem I'm coming up with
is the FilterInvoices method errors with "No supported
translation to sql" because of the GetInvoice method
which is iused to return an Invoice entity (this is
a layer on top op the LInq2 sql layer) and not an Linq2sql Invoice entity.
So how do I return a IQueryable from my service with this structure??
Also how do I sort and return a IQureyable in repository GetInvoices.
Hope this makes sense.
Malcolm
linq2sql thinks GetInvoice (within GetInvoices) is a stored procedure. One way around it
var query = from inv in _dbctx.Invoices orderby inv.invInvoiceDate descending select inv ;
though that would pass back the objects generated by your datacontext. If you wanted to populated custom objects you could iterated over the collection creating your custom Invoice objects and populating them.
foreach(var inv in query) { somelist.Add(new MyCustomInvoince() { id = inv.id ... }
EDIT: The above will return a list. Use the following to return IQueryable
return from item in query
select GetInvoice(item);
The difference is at this stage your are using Linq2Objects, and that provider will know how to call GetInvoice
You cannot query with LTS (Linq to SQL) something built 'on top' of the LTS layer.
The reason is that the LTS entities layer is a mapping of the content of you database, and the query you perform is 'just' translated into SQL.
I personnaly use another approach to keep an independance between my layers...
I create interfaces that match my LTS entites, and I use the Cast<>() method to have my repository return the interface instead of the concrete implementation.
It works perfectly.
You need to extend the base entity (no pb as it is a partial class) :
partial class Employee : IEmployee
And you need this property in your repository :
public IQueryable<IEmployee> Query
{
get
{
return this._context.Employees.Cast<IEmployee>();
}
}
Based on this, you could code a generic repository, but it's another story (more complicated)
This is just off the top of my head, but you could try:
from i in _service.GetInvoices().AsEnumerable()