Get Entity by Name - c#

Say I have a list of EF entity names like:
List<string> entityNames = new List<string>(){
"Table1",
"Table2",
"Table3"
};
From this list of entities I want to query each entity individually, similar to:
var result = efContext.Table1.Where(t => ...);
Using reflection, or black magic, how would I obtain a reference to the actual entity so that I could wind up with something like:
foreach(var e in entityNames)
{
var entity = efcontext.GetType().GetProperties().Where(t => t.Name == e).Single();
var result = efContext.entity.Where(t => ...);
}
Thoughts?

Supposing that all of the Entity types listed implement some common interface that you intend to use in your Where clause, you could do something like this:
foreach(var e in entityNames)
{
PropertyInfo entityProperty = efcontext.GetType().GetProperties().Where(t => t.Name == e).Single();
var baseQuery = (IQueryable<IMyEntity>)entity.GetValue(efContext, null);
var result = baseQuery.Where(t => ...);
}

Related

Select by ID from a list

I have a table of products and a table of categories, I can select by the ID of the Category like this:
var result = db.tblProducts.Where(p => p.tblCategories.Any(c => c.ID == 1));
However, I want to be able to select based on a list of Categories:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => r.tblCategories.Any(t => catIDs.Contains(t.ID)));
I get the following error:
LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method, and this method cannot be translated into a store expression.
Presumably because I am using Contains to compare entities to local variables. Is there a way to do this?
Try create Expression from values. F.e.:
static Expression MakeOrExpression<T, P>(Expression<Func<T, P>> whatToCompare, IEnumerable<P> values)
{
Expression result = Expression.Constant(true);
foreach (var value in values)
{
var comparison = Expression.Equal(whatToCompare, Expression.Constant(value));
result = Expression.Or(result, comparison);
}
return result;
}
How to use:
var results = db.tblProducts.Where(r => r.tblCategories.Any(MakeOrExpression(t => t.ID, catIDs)));
The method MakeOrExpression will create an expression t.ID == 1 || t.ID == 2 || t.ID == 3 for list { 1, 2, 3 } dynamically, and then EF will translate it to SQL condition.
Maybe you can use this:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblCategories
.Where(t => catIDs.Contains(t.ID))
.SelectMany(t => t.tblProducts)
.Distinct();
Try this:
var query=from p in db.tblProducts
from c in p.tblCategories
where catIDs.Contains(c.ID)
select p;
If at least one of the categories of the product is in the catIDs list, then the product will be seleted.
Another option could be start by the categories (I'm guessing you have a many to many relationship between Product and Category and you have a collections of products in your Category entity):
var query=db.tblCategories.Where(c => catIDs.Contains(c.ID)).SelectMany(c=>c.tblProducts).Distinct();
Try this code :
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => catIDs.Any(c => c == r.tblCategories.Id));

Entity Framework LINQ Include - Child Entities

I am loading the child entity UserStarted from my TransactionDetails
var result = (from A in context.Transactions.Include(_ => _.TransactionDetails.Select(us => us.UserStarted))
select A).SingleOrDefault();
Now I want to load another entity, but I don't know how. The entity is similar to UserStarted, it's the UserEnded also from TransactionDetails
Thank you very much
You can chain multiple include predicates together:
var result = context.Transactions
.Include(transaction => transaction.TransactionDetails.Select(us => us.UserStarted))
.Include(transaction => transaction.TransactionDetails.Select(us => us.UserEnded))
.SingleOrDefault();
You can also try
var result = (from A in context.Transactions.Include(_ => _.TransactionDetails.Select(us => new { UserStarted = us.UserStarted, UserEnded = us.UserEnded }))
select A).SingleOrDefault();

Linq ordering both parent and child related entities

I have a parent child model where I want to sort by the SortOrder column of both entities. I have the query working but it seems overly verbose and I was wondering if there was a simpler solution to this problem.
I am initially loading the results into an anonymous type (as you can not load complex types directly into the entity framework entities) then querying that type again to load into the entities. I know I could simplify this by implementing a DTO but was interested in a cleaner solution for this use case.
Model
Query
public List<Group> GetStaticMeasures(int businessUnitID)
{
var groups = (from g in ctx.Groups.Where(w => w.BusinessUnitID.Equals(businessUnitID)).OrderBy(o => o.SortOrder)
select new
{
ID = g.ID,
BusinessUnitID = g.BusinessUnitID,
Name = g.Name,
SortOrder = g.SortOrder,
Datapoints = (from d in ctx.Datapoints where g.ID.Equals(d.StaticGroupID) orderby d.SortOrder select d).ToList()
}).ToList();
var results = from g in groups
select new Group
{
ID = g.ID,
BusinessUnitID = g.BusinessUnitID,
Name = g.Name,
SortOrder = g.SortOrder,
Datapoints = g.Datapoints
};
return results.ToList();
}
How about:
public IEnumerable<Group> GetStaticMeasures(int businessUnitID)
{
var groups = ctx.Groups
.Include("Datapoints")
.Where(w => w.BusinessUnitID.Equals(businessUnitID))
.OrderBy(o => o.SortOrder);
foreach(var g in groups)
{
g.Datapoints = g.Datapoints.OrderBy(d => d.SortOrder).ToList();
yield return g;
}
}

Linq to Sql: Optimizing lamba expression - clean code

I have this expression, which generates single query to the database:
db = new MyDataContext();
var productInCity = db.Products
.Where(n => n.id == 2)
.Select(k => new ProductInCityDto()
{
ProductName = k.ProductName,
CityName = k.Store.City.Name,
CountryName = k.Store.City.Country.Name
.
.
.
})
.FirstOrDefault();
I want to make this code cleaner, by putting the mapping in a function, extension method or in the object's constructor, something like this:
db = new MyDataContext();
var productInCity = db.Products
.Where(n => n.id == 2)
.Select(k => new ProductInCityDto(k))
.FirstOrDefault();
But, in this case, multiple queries to the DB are generated (I use LinqToSql Profiler).
Is there a way to isolate the mapping (Select statement) in order to achieve better code readability?
YES, if you look at the actual signature of the Select extension method on IQueryable you will find that it does not take a function but an Expression>.
So, just do that...
Expression<Func<Product, ProductInCityDto>> MyMappingExpression
{
get
{
return product => new ProductInCityDto
{
...
}
}
}
and then
db = new MyDataContext();
var productInCity = db.Products.Where(n => n.id == 2)
.Select(MyMappingExpression)
.FirstOrDefault();
If you need to use MyMappingExpression in process you will likely want to convert it to a
Func<Product, ProductInCityDto>
by calling the Expression.Compile() method.
Instead of creating mappings by hand, you may use AutoMapper. But if you do not want to create the mapping using a third party tool just change the query to the following;
var productInCity = new ProductInCity(
db.Products.Include("Store").SingleOrDefault(n => n.id == 2));

many to one linq projection

How can I write this in one query?
var query = this.ObjectContext.SomeCollection.
.Where(...)
.Select(f => new {somenumber = f.somenumber});
MyType type = new MyType()
{
Sum = query.Sum(f => f.somenumber)
}
Your use of the anonymous type is completely unnecessary since you have only one property in the projection. You can simply take the query and enclose it inside the object initializer for MyType. Note that this is fine as long as you're not reusing the projection elsewhere (in which case you would pull it outside and then reuse it).
var type = new MyType {
Sum = this.ObjectContext
.SomeCollection
.Where(SomeCondition)
.Select(f => f.somenumber)
.Sum()
};
Additionally, you could reduce .Select(f => f.somenumber).Sum() to Sum(f => f.somenumber).
MyType type = new MyType {
Sum =
this.ObjectContext.SomeCollection
.Where(...)
.Select(f => f.somenumber)
.Sum() };
or even
MyType type = new MyType {
Sum =
this.ObjectContext.SomeCollection
.Where(...)
.Sum(f => f.somenumber) };
shoud do the trick

Categories