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;
}
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;
}
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();
}
I am trying to refactor Linq to Entity query to prevent writing duplicate code but I can't avoid a "LINQ to Entities does not recognize the method" exception. The reason is IsUserActive method.
I am providing the code below as example
public static bool IsUserActive(this User user)
{
return user.Orders.Any(c => c.Active && (c.TransactionType == TransactionType.Order || c.TransactionType == TransactionType.Subscription));
}
public IQueryable<UserView> GetView()
{
return context.users.Select(p => new UserView
{
Id = p.Id,
Active =p.IsUserActive()
});
}
Is it possible to refactor Linq code to prevent duplication in my situation?
I can't use IList instead of IQueryable.
I know why this exception happens, no need to explain.
If you want to use your method in a lot of places, I don't think you can do anything except store it as an expression and then referring to that expression wherever you were calling the original method:
public static Expression<Func<User, bool>> IsUserActive = user =>
user.Orders.Any(c => c.Active &&
(c.TransactionType == TransactionType.Order ||
c.TransactionType == TransactionType.Subscription));
public IQueryable<UserView> GetView()
{
return context.users.Select(p => new UserView
{
Id = p.Id,
Active = IsUserActive(p)
});
}
EDIT: OK, so this doesn't work. You can't just "call" an expression like this. The only alternative I can think of is to turn your whole Select parameter into an expression:
public static Expression<Func<User, UserView>> UserToUserView = user =>
new UserView
{
Id = user.Id,
Active = user.Orders.Any(c => c.Active &&
(c.TransactionType == TransactionType.Order ||
c.TransactionType == TransactionType.Subscription)
};
public IQueryable<UserView> GetView()
{
return context.users.Select(UserToUserView);
}
This is little tricky, but this is how we are doing it. You can combine multiple IQueryables into one as shown below.
public static IQueryable<Order> ActiveOrdersQuery(this MyDBEntities db)
{
return db.orders.Where(
o=> o.Active &&
(o.TransactionType == TransactionType.Order ||
o.TransactionType == TransactionType.Subscription )));
}
public IQueryable<UserView> GetView()
{
var q = context.ActiveOrdersQuery();
return context.users.Select( x=> new UserView{
Id = x.Id,
Active = q.Any( o=> o.UserId == x.Id)
});
}
The only problem is you have to explicitly compare foreign keys. But this results in exactly same SQL and will perform same.
This way you can refactor common queries and use them inside your views.
If I have the following how to do I cast the result of the lambda expression back to the Customer type from IEnumerable<Customer> without having to iterate over it.
public class Customer : CustomerModel
{
public List<Customer> CustomerList {get;set;}
public Customer GetCustomerFromListById(long id)
{
return CustomerList.Select(c => c).Where(i => i.Id == id);
}
}
Use .Single(), also Select is redundant:
return CustomerList.Single(i => i.Id == id);
As mentioned in the comments that way you get an exception if it doesn't contain the needed key, which should be the expected behavior.
Use FirstOrDefault().
return CustomerList.Select(c => c).Where(i => i.Id == id).FirstOrDefault();
I'm looking for an equivalent of the DataContext.GetTable<TEntity> in Entity Framework.
I've found the ObjectContext.CreateQuery<T> method but it is different from DataContext.GetTable<TEntity> since it needs a querystring to work.
Is there a way to get an IQueryable object for a table using the entity type without specifying the querystring?
*EDIT: Added code snippet*
This is a snippet of a Repository class I've implemented that works with linq2sql. I can't use ObjectContext.[TableName] because it wouldn't be generic anymore.
public class BaseRepository<TClass> : IDisposable
where TClass : class
{
protected BaseRepository(DataContext database)
{
_database = database;
}
...
public IQueryable<TClass> GetAllEntities()
{
IQueryable<TClass> entities = _database.GetTable<TClass>();
return entities;
}
public IQueryable<TClass> GetEntities(Expression<Func<TClass, bool>> condition)
{
IQueryable<TClass> table = _database.GetTable<TClass>();
return table.Where(condition);
}
*EDIT: Added my solution (so far..)*
This is what I'm using:
public IQueryable<TClass> GetEntities(Expression<Func<TClass, bool>> condition)
{
IQueryable<TClass> table = _database.CreateQuery<TClass>(typeof(TClass).Name);
return table.Where(condition);
}
This works as long as the class name is the same of the table name. This will became a problem for me when I'll start using different objects for the same table.
I hope I've been clear, thanks in advance,
Marco :)
Actually, the EF designer itself uses CreateQuery with hard-coded strings for the static references. If you dig into the designer file you'll see something like this:
public global::System.Data.Objects.ObjectQuery<Customers> Customers
{
get
{
if ((this._Customers == null))
{
this._Customers = base.CreateQuery<Customers>("[Customers]");
}
return this._Customers;
}
}
private global::System.Data.Objects.ObjectQuery<Customers> _Customers;
Technically there's no perfect solution because you can use the same entity type for different entity sets. But you can give it the old college try:
public IQueryable<TEntity> GetEntities<TEntity>()
{
Type t = typeof(TEntity);
var edmAttr = (EdmEntityTypeAttribute)Attribute.GetCustomAttribute(t,
typeof(EdmEntityTypeAttribute), false);
if (edmAttr == null) // Fall back to the naive way
{
return context.CreateQuery<TEntity>(t.Name);
}
var ec = context.MetadataWorkspace.GetEntityContainer(
context.DefaultContainerName, DataSpace.CSpace);
var entityType = context.MetadataWorkspace.GetType(edmAttr.Name,
edmAttr.NamespaceName, DataSpace.CSpace);
var es = ec.BaseEntitySets.First(es => es.ElementType == entityType);
return context.CreateQuery<TEntity>(es.Name);
}
public IQueryable GetTable<T>(T entity) where T : class
{
return context.CreateObjectSet<T>();
}
I hope I'm not missing the point, but wouldn't it be:
ObjectContext.TableName
Where TableName is the EntitySet of the type you want to work with.