dynamic linq queries in linq-to-entities - linq

what I want is to include 3 tables in my query and select only the last table fields.
[WebMethod]
public Project[] GetAlll(int passeid)//passed id is the id of fieldteammeber table I am passing
{
var arr = db.Project.Include("FieldTeamMember")
.Include("FieldTeam")
.Where(ft_id=ftm_id and ft_mid=prj_ftm_id and FTM_ID=passeid)
.ToArray();
return arr;
}
want to select the project table fields. there are F keys between tables in the model.

I think this is the query you're looking for based on your description:
int memberId = 1;
var projects = db.Projects
.Where(p => p.FieldTeam
.FieldTeamMembers.Any(ftm => ftm.Id == memberId));
Alternatively, if this is more readable to you:
int fieldTeamMemberId = 1;
var projects = db.FieldTeamMembers.Where(ftm => ftm.Id == memberId)
.Select(ftm => ftm.FieldTeam.Project)
.Distinct();

Related

.net core linq select overload index does not work

I got a linq lambda select code that works before I added the Select index overload. Before, I got the list of records but I need the index which I use to assign a unique Id to each record. When I add with ToList(), I get an exception with no error/inner exception. Only way I can get the code to not throw an error is to use .AsEnumberable() but I need a list. I read many post that .ToList() works with the overload but I have been unsuccessful.
Here is my code and my attempt to fix this
var emps = this.DbContext.Employees
.GroupJoin(this.DbContext.Depts,
employee => employee.EmployeeId,
dept => dept.EmployeeId,
(employee, dept) => new { employee, dept }
)
.SelectMany(
employee_dept_left => employee_dept_left.dept.DefaultIfEmpty(),
(employee_dept_left, dept) => new { employee_dept_left, dept }
)
.Join(this.DbContext.Divs,
emp_emp_dept => emp_emp_dept.employee_dept_left.employee.DivId,
division => division.DivId,
(emp_emp_dept, division) => new { emp_emp_dept, division }
)
.Where(s => !string.IsNullOrEmpty(filter.selectedDiv))
.GroupBy(grouped => new
{
grouped.emp_emp_dept.employee_dept_left.employee.EmployeeId,
grouped.emp_emp_dept.employee_dept_left.employee.LastNm,
grouped.emp_emp_dept.employee_dept_left.employee.FirstNm,
grouped.emp_emp_dept.employee_dept_left.employee.DivId
})
.Select((joined, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivId,
}).ToList();
The error message says
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 using .AsEnumerable() instead of .ToList():
List<EmployeeViewModel> test = emps.Cast<EmployeeViewModel>().ToList();
but this throws an exception.
Any help is greatly appreciated.
Thanks in advance
Problem that this Select is not currently translatable to the SQL. You can make additional Select to solve issue with AsEnumerable().
...
.Select(joined => new
{
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivisionId,
})
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();
And note that query is more readable in Query syntax when there are joins.
var query =
from employee in this.DbTracsContext.Employees
join dept in his.DbTracsContext.Depts on employee.EmployeeId equals dept.EmployeeId into employee_dept_left
from dept in employee_dept_left.DefaultIfEmpty()
join division in this.DbTracsContext.Depts on employee.DivisionId equals division.DivisionId
where string.IsNullOrEmpty(filter.DivisionSelection) || filter.DivisionSelection == "0" || employee.DivisionId == filter.DivisionSelection
group employee by new { employee.EmployeeId, employee.LastNm, employee.FirstNm, employee.DivisionId } into g
select new
{
EmployeeId = g.Key.EmployeeId,
LastNm = g.Key.LastNm.Trim(),
FirstNm = g.Key.FirstNm.Trim(),
DivisionId = g.Key.DivisionId,
};
var emps = query
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();

Select only a single column in LINQ

The EntityModel is defined as:
Personnel has a link to a Country
When executing this code in LinqPad, I see that the SQL which is generated is not optimized (all fields are returned) in the first query ? What am I missing here or doing wrong ?
Query 1 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();
Query 1 SQL
exec sp_executesql N'SELECT [t0].[Id], [t0].[Version], [t0].[Identifier], [t0].[Name], , [t0].[UpdatedBy] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
Query 2 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds2 = Personnels.Where(p => p.Country == Country).Select(p => p.Id).ToArray();
personnelIds2.Dump();
Query 2 SQL
exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
The database used is SQL Express 2008. And LinqPad version is 4.43.06
//var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = context.Personnels
.Where(p => p.Country.Id == 100000581)
.Select(p => p.Id)
.ToArray();
personnelIds.Dump();
Try this, it should be better.
Personnels collection will be populated via lazy loading when accessed, hence retrieving all of the fields from the DB. Here's what's happening...
// retrieves data and builds the single Country entity (if not null result)
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
// Country.Personnels accessor will lazy load and construct all Personnel entity objects related to this country entity object
// hence loading all of the fields
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
You want something more like this:
// build base query projecting desired data
var personnelIdsQuery = dbContext.Countries
.Where( c => c.Id == 100000581 )
.Select( c => new
{
CountryId = c.Id,
PersonnelIds = c.Personnels.Select( p => p.Id )
}
// now do enumeration
// your example shows FirstOrDefault without OrderBy
// either use SingleOrDefault or specify an OrderBy prior to using FirstOrDefaul
var result = personnelIdsQuery.OrderBy( item => item.CountryId ).FirstOrDefault();
OR:
var result = personnelIdsQuery.SingleOrDefault();
Then get the array of IDs if not null
if( null != result )
{
var personnelIds = result.PersonnelIds;
}
Try can also try grouping personnel into a single query
var groups =
(from p in Personnel
group p by p.CountryId into g
select new
{
CountryId = g.Key
PersonnelIds = p.Select(x => x.Id)
});
var personnelIds = groups.FirstOrDefault(g => g.Key == 100000581);
Do you have the ForeignKey explicitly defined in your POCO for Personnel? It's common to leave it out in EF, but adding it would massively simplify both this code and the resulting SQL:
public class Personnel
{
public Country Country { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
. . .
}
> update-database -f -verbose
var ids = db.Personnel.Where(p => p.CountryId == 100000581).Select(p => p.Id).ToArray();

Dynamic Where clauses with multiple joins using Linq

I need to build a dynamic where clause in a Linq statement with multiple joins.
.Net 3.5
Linq-To-Sql
I have these incoming parameters for the Linq statement, only the "UID" is required.
int uid = 23702; // <-- Only one Required
string courseNumber = "";
string title = "";
int? categoryId = null;
int? typeId = null;
I've been testing this out in LinqPad and while I've gotten the query to work with all Where clauses in place, the Nullable int parameters end up returning incorrect results.
Here's my Linq statement:
var ci = course_instances;
var query = courses.Join(ci,
c => c.course_id,
i => i.course_id,
(c, i) => new
{
c = c,
i = i
}).Join(user_courses,
temp => temp.i.instance_id,
uc => uc.instance_id,
(temp, uc) => new
{
temp = temp,
uc = uc
})
.Where (temp1 => (temp1.uc.uid == uid))
.Where (temp1 => (temp1.temp.c.course_number.Contains(courseNumber)))
.Where (temp1 => (temp1.temp.c.title.Contains(title)))
//.Where (temp1 => (temp1.temp.c.course_type_id == typeId))
//.Where (temp1 => (temp1.temp.c.course_category_id == categoryId))
.Select (temp1 => new CourseSearchMyCourses
{
// snipped the many properties
});
I've tried using PredicateBuilder, but it returns the error:
The type arguments for method 'System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Here's my PredicateBuilder Linq attempt:
var conditions = PredicateBuilder.True<user_course>();
conditions = conditions.And(c => c.uid == uid);
var ci = course_instances;
var query = courses.Join(ci,
c => c.course_id,
i => i.course_id,
(c, i) => new
{
c = c,
i = i
}).Join(user_courses,
temp => temp.i.instance_id,
uc => uc.instance_id,
(temp, uc) => new
{
temp = temp,
uc = uc
})
.Where (conditions)
.Select (temp1 => new CourseSearchMyCourses
{
// snipped the many properties
});
BTW, I also tried using "System.Linq.Dynamic" using string queries, and got the error the " and " isn't recognized.
Any help is appreciated.
Thanks.
Linq predicates with nullable type variables get translated into a SQL predicate = NULL. But that is totally different than what it should be: IS NULL.
You expect to get the rows where course_type_id is empty, but the = comparison returns no results because NULL is not a value and the comparison returns UNKNOWN. I think that is the cause of your "incorrect results".
If this is your problem, a fix can be found here.

EF Code First selecting rows based on many to many relationship

I have the following code in my repository:
public PagedResult<Post> GetAllPublishedByTag(int tagId, int start, int max)
{
var query = Database.Set<Post>().Where(p => p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
int total = query.Count();
var result = query.ToList();
return new PagedResult<Post>(result, total);
}
This will give me all published posts. But what I want is selecting all published posts for a certain tag. My model is setup in such a way that tags have a many to many relationship to posts. I tried to slightly modify the above code but this did not work:
public PagedResult<Post> GetAllPublishedByTag(Tag tag, int start, int max)
{
var query = Database.Set<Post>().Where(p => p.Tags.Contains(tag) && p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
int total = query.Count();
var result = query.ToList();
return new PagedResult<Post>(result, total);
}
I would prefer to pass in the tagId (as per the first code example) as opposed to the tag object but not sure how to correctly write the LINQ statement.
var query = Database.Set<Post>().Where(p => p.Tags.Any(t => t.Id == tagId) && p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
Side Note: I believe you may have issues with your pagination, as the variable total is calculated after skip/take are called.

LINQ: Group By + Where in clause

I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};

Resources