Where is client evaluation needed? - entity-framework

I have a query which works fine in Linq to Objects in this fiddle:
var list = from order in orders
join detail in details
on order.id equals detail.order into od
select new { order = order, details = od };
I tried applying the same query when the data is in a database (note I am mapping Linq to Sql manually):
public class dbContext: DbContext {
public DbSet<Order> Orders { get; set; }
public DbSet<Detail> Details { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder oB) {
oB.UseSqlServer("...connection string...");
}
}
using (var db = new dbContext() {
var list = from order in db.Orders
join detail in db.Details
on order.id equals detail.order into orderDetails
select new { order = order, details = orderDetails };
}
The above gives:
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
I tried details = orderDetails.ToList() in the last line but the same error is there. Where should I add the manual client evaluation?
Some background information: the following database query (without the into) works fine:
var list = from order in db.Orders
join detail in db.Details
on order.id equals detail.order
select new { order = order, detail = detail };

Instead of a join you should declare Navigation Properties and use something like:
var query = from order in db.Orders
select new { order = order, details = order.OrderDetails };
var list = query.ToList();
or simply
var list = db.Orders.Include(o => o.OrderDetails).ToList();

Related

Entity Framework Core generates wrong query for MySQL when I use separate IQueryable generic method

I faced with a strange behaviour when entity framework core throwing away my Includes.
I need to make some generic methods to filter and combine my queries and those methods must receive some parameters and IQueryable filter subquery and return combined IQueryable result for further composition.
I simplified my code and made an example when you can see what I do mean:
public IQueryable<Tuple<TResult, TFilter>> Method1<TResult, TFilter>(IQueryable<TFilter> filters)
where TResult : ResultEntity
where TFilter : FilterEntity
{
var q = from state in _dbContext.Set<TResult>()
join f in filters on state.ID_Result equals f.ID
where ....
select new Tuple<TResult, TFilter>(state, f);
return q;
}
public void GetResult(int pageIndex, int pageSize)
{
IQueryable<Car> results = _dbContext.Cars.Include(x => x.Images);
// 1) All right, it has the images
var q1 = Method1<CarState, Car>(results).ToList();
// 2) Wrong, there is no images. And SQL query doesn't contain any joins to Images table
var q2 = Method1<CarState, Car>(results).Skip(pageIndex).Take(pageSize).ToList();
// 3) Without the method and with anonymous type it's ok.
var tmp = from state in _dbContext.Set<CarState>()
join f in results on state.ID_Result equals f.ID
where ....
select new { state, f };
var q3 = tmp.Skip(pageIndex).Take(pageSize).ToList();
}
What did I do wrong?
Do not use Builder methods or constructors if you want to use query later. It is because LINQ translator cannot trace back fields to generate correct SQL.
This part of query is problematic
new Tuple<TResult, TFilter>(state, f);
Better to create new class(es)
public class MTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
And use them in your methods (similar to anonymous classes usage)
public IQueryable<MTuple<TResult, TFilter>> Method1<TResult, TFilter>(IQueryable<TFilter> filters)
where TResult : ResultEntity
where TFilter : FilterEntity
{
var q = from state in _dbContext.Set<TResult>()
join f in filters on state.ID_Result equals f.ID
where ....
select new MTuple<TResult, TFilter>
{
Item1 = state,
Item2 = f
};
return q;
}
SUGGESTION
Such classes can be generated by T4 template, or just copy generated code

How to join tables using linq and entity framework

In the code given below i want to join all the three table i get the data by joining the table but while displaying the data only the data of CK_Model is displayed. Please help
public List<CK_Model> GetDetails()
{
try
{
using (var entities = new MobileStore2020Entities())
{
var details = from a in entities.CK_Model
join b in entities.CK_Brand
on a.BrandID equals b.BrandID
join c in entities.CK_Stock
on a.ModelID equals c.ModelID
select new
{
ModelID = a.ModelID,
ModelName = a.ModelName
};
return details.ToList();
Thank You.
If I understand correctly you want to return data from all 3 tables by accessing them through your context. If you want to do this you have change your method's return type. For example create a new class with all 3 item types
public class DetailsItemType
{
public CK_Model Model{ get; set; }
public CK_Brand Brand { get; set; }
public CK_Stock Stock { get; set; }
}
Then change your method's return type to DetailsItemType and you'll have something like the following
public List<DetailsItemType> GetDetails()
{
using (var entities = new MobileStore2020Entities())
{
var details = from a in entities.CK_Model
join b in entities.CK_Brand
on a.BrandID equals b.BrandID
join c in entities.CK_Stock
on a.ModelID equals c.ModelID
select new DetailsItemType
{
Model= a,
Brand = b,
Stock = c
};
return details.ToList();
}
}
Now every time you call GetDetails() you can access all 3 tables. For example
var details = GetDetails();
var model = details.Model;
var brand = details.Brand;
var stock = details.Brand;

Entity Framework Core Include Then Left Join Fluent API

I have a structure of classes that follow logic similar to this...
DBContext1
public class Parent {
public int Id
public ICollection<Child> child
}
public class Child {
public int Id
public string Item
public ICollection<Action> action
}
public class Action {
public int Id
}
This is a one to many to many relationship. I created these tables using entity framework and everything is working well.
My problem is that I want to do a left join from my class child to an object in a different dbContext. Most of the examples I have seen all do the left join at the parent class level.
For example in DBContext2 I have a class that contains item information. I want to perform a left join on the item property from the child class in DBContext1.
DBContext2
public class Item {
public string Item
public string Description
}
This below gets me my Parent and Child objects joined together but how do I expand it to do a left join on the item class object
using (var dbcontext1 = new DBContext1())
{
using (var dbcontext2 = new DBContext2())
{
var parent = dbcontext1.Parent
.Include(ch => ch.Child)
//maybe do groupjoin here... would join parent.Child.item = dbcontext2.item.item
.ToList();
}
}
I have tried doing a GroupJoin but it does not seem to work. I want to use the Fluent pattern instead of using the "from p in Parent" syntax. Is there a way to do this?
Edit 1:
using (var dbcontext1 = new DBContext1())
{
using (var dbcontext2 = new DBContext2())
{
var parent = dbcontext1.Parent
.Include(ch => ch.Child)
.ToList();
var item = dbcontext2.Item.ToList()
var query = from p in parent
join i in item on p.Child.Item == i.item //how do I do the join correctly?
select {
//select whatever here
}
}
}
Comments say to do this in two SQL statements. Running with this approach but struggling how to do the join from the Parent.Child.Item to Item.Item

Clear DataGridView datasource linked to Entity Framework

I'm new to Entity Framework, I have a DataGridView connected to Entity Framework as it shown below
dgvDebit.DataSource = (from daily in accountingEntities.DailyAccounts
join voucherdetails in accountingEntities.VoucherDetails on daily.DailyId equals voucherdetails.DailyId
where voucherdetails.VoucherId == keyvalue
group voucherdetails by daily.DailyName into dgroup
select new
{
DailyName = dgroup.Key,
SumOfDebit = dgroup.Sum(s => s.Debit)
}).ToList();
My question is: I want to clear DataGridView datasource but every thing I did has failed - please any help here?
OK, so you want to bind to an empty list of the type that you have.
Step 1: define a .NET type for your query result return type:
public class ResultDTO
{
public string DailyName { get; set; }
public decimal SumOfDebit { get; set; }
}
Step 2: change your "normal" query to:
dgvDebit.DataSource = (from daily in accountingEntities.DailyAccounts
join voucherdetails in accountingEntities.VoucherDetails on daily.DailyId equals voucherdetails.DailyId
where voucherdetails.VoucherId == keyvalue
group voucherdetails by daily.DailyName into dgroup
select new ResultDTO
{
DailyName = dgroup.Key,
SumOfDebit = dgroup.Sum(s => s.Debit)
}).ToList();
Step 3: if you want to "clear" the DataGridView, but retain the columns, you can now use:
dgvDebit.DataSource = new List<ResultDTO>();

EF core paging. Select total count in same query

I have paging and i want to select count in the same query using simple sql (EF7):
var selectSql = " SELECT TotalCount = COUNT(*) OVER(), E.* FROM [table] E ...";
var rows = context.Set<EventTable>().FromSql<EventTable>(selectSql, parameters.Select(p => p.Value).ToArray()).ToArray();
This select works, but i don't have TotalCount property in my EventTable class, because i don't want that property in database.
I try get TotalCount property from entity tracker:
var row = rows.First();
var entity = context.Entry(row);
var totalCount = entity.Property<int>("TotalCount").CurrentValue;
But then i get error:
The property 'TotalCount' on entity type 'EventTable' could not be found. Ensure that the property exists and has been included in the model.
Then i try to add property in model like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EventTable>(b => b.Property<int>("TotalCount"));
}
It works fine when i want to select, but it throws an exception on insert, because column in database not exist.
And EF will add that column on migration. But i notice, that if before migration generation i add line b.Property("TotalCount"); into ModelSnapshot class it will avoid to add property on migration. But problem on insert still exist.
I try to create another class:
[NotMapped]
public class EventSearchTable : EventTable
{
[Column("total_count")]
[Required]
public int TotalCount { get; set; }
}
and then do this:
var rows = context.Set<EventSearchTable>().FromSql<EventSearchTable>(..);
It works on EF6, but not on EF7, i got error: Value cannot be null.
Parameter name: entityType Because no entity in my DbContext. If i will add EventSearchTable class on my DbContext then it will expect columns like discriminator and etc and will create table in migrations.
Any ideas how to get property TotalCount ?
The following query will get the count and page results in one trip to the database
var query = context.Set<EventTable>();
var page = query.OrderBy(e => e.Id)
.Select(e => e)
.Skip(100).Take(100)
.GroupBy(e => new { Total = query.Count() })
.FirstOrDefault();
if (page != null)
{
int total = page.Key.Total;
List<EventTable> events = page.Select(e => e).ToList();
}