restrict num of items loaded in navigation property - c#

I have the following query in Linq:
var query = from question in context.Questions.Include("QAnswers")
join answer in context.Answers
on question.id equals answer.qst
where answer.user == param_userID
select question;
return query.toList();
The problem is that it doesn't load the "QAnswers" navigation property at all.
Is there any way in Entity Framework to return the entity and restricted result set in its navigation property?
Ps.
I'm using EF4 if it's important

If I understood it correctly, this should be enough:
context.Questions.Include("QAnswers").Where(q => q.QAnswers.Any(a => a.user == param_userID));
These are questions where specific user answered. There is no need for joins.
EDIT
context.Questions.Where(q => q.QAnswers.Any(a => a.user == param_userID)).Select(q => new { Question = q, Answers = q.QAnswers.Where(a => a.user == param_userID) }).ToList();
This will return question and only specific user's answers.

Related

LINQ troubles in C# using Entity Framework

I have a few tables and this is what I need to achieve.
This gets all the rows from one table
var FRA = from prod in _cctDBContext.Fra
where prod.ActTypeId == 1
From within that, I get all the rows where ActTypeID.
Then I need to query another table from with the ID's get from that
foreach (var item in FRA)
{
var FRSA = _cctDBContext.Frsa
.Select(p => new { p.Fraid, p.Frsa1,
p.Frsaid, p.CoreId,
p.RelToEstId, p.ScopingSrc,
p.Mandatory })
.Where(p => p.Fraid == item.Fraid)
.ToList();
}
I then need to push each one of these to Entity Framework. I usually do it this way:
foreach (var item in FRA)
{
var FinanicalReportingActivity = new FinancialReportingActivity { FinancialReportingActivityId = item.Fraid, ScopingSourceType = item.ScopingSrc, Name = item.Fra1, MandatoryIndicator = item.Mandatory, WorkEffortTypeId = 0 };
_clDBContext.FinancialReportingActivity.AddRange(FinanicalReportingActivity);
}
But because I have used 2 for each loops, I cannot get the variables to work because I cannot find a way to get local variables as the entity context.
Can anyone think of a better way to code this?
Thanks
It looks like you can do this as a single join:
var query =
from prod in _cctDBContext.Fra
where prod.ActTypeId == 1
join p in _cctDBContext.Frsa on prod.Fraid equals p.Fraid
select new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
};
It looks like you are loading data from one set of entities from one database and want to create matching similar entities in another database.
Navigation properties would help considerably here. Frsa appear to be a child collection under a Fra, so this could be (if not already) wired up as a collection within the Fra entity:
Then you only need to conduct a single query and have access to each Fra and it's associated Frsa details. In your case you look to be more interested in the associated FRSA details to populate this ReportingActivity:
var details = _cctDBContext.Fra
.Where(x => x.ActTypeId == 1)
.SelectMany(x => x.Frsa.Select(p => new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
}).ToList();
though if the relationship is bi-directional where a Fra contains Frsas, and a Frsa contains a reference back to the Fra, then this could be simplified to:
var details = _cctDBContext.Frsa
.Where(x => x.Fra.ActTypeId == 1)
.Select(p => new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
}).ToList();
Either of those should give you the details from the FRSA to populate your reporting entity.

How to convert MySQL Group By to C# LINQ Query? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have the following code in MySQL
SELECT Query,SUM(TotalResults)
FROM TotalJobResults
GROUP BY Query
ORDER BY `SUM(TotalResults)` DESC
I am trying to duplicate this function calling a MongoDB Collection in C# and cannot seem to actually understand it very well. I have the code below but it seems far from answering the problem,
var t = from q in _jobCollection.Find(_ => true).ToList().GroupBy(
p => p.Query,
p => p.TotalResults,
(key, g) => new { Query = key, Total = g.ToList().Sum() }
);
Thanks for any light you can shed on this!
Looks like it works if I put a select q on the end. hmm! my error
var t = from q in _jobCollection.Find(_ => true).ToList().GroupBy(
p => p.Query,
p => p.TotalResults,
(key, g) => new { Query = key, Total = g.ToList().Sum() }
) select q;

how to use joining with LinQ To Entity [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have this code
var value = (from dc in _context.ContractDetails
where dc.EmployeeID == id
select dc.Amount);
return value;
}
is it acceptable to do Value.Sum();
You want to return the sum it looks like. Instead of having query be a decimal, just let it be what it wants (var, it's really IEnumerable<decimal>). Then you can return an aggregate on that. Sum for example
var query = from emp in Employees
join cd in ContractDetails
on emp.EmployeeID equals cd.EmployeeID
where cd.EmployeeID == id
select cd.Amount;
return query.Sum();
If this is all it does, then I also feel like you don't need to join at all, and it would be simpler to do
var query = from cd in ContractDetails
where cd.EmployeeID == id
select cd.Amount;
return query.Sum();
... unless you were using the join to test for the existence of an employee in the Employee table as a condition.
Your linq statement results in an IQueryable<Amount>, you would need to take that result and call Sum() on it to get the result you're seeking.
First, isn't there a navigation property you can use (i.e. Employee.ContracteDetails) instead of manually joining the two sets? For example,
var sum = _context.Employee
.Where( e => e.Id == id )
.Select( e => e.ContractDetails.Sum( cd => cd.Amount ) )
.SingleOrDefault();
Second, you're not using any information you need from Employee, even your where clause references ContractDetails alone; why start your query there? Work with _context.ContractDetails instead:
var sum = _context.ContractDetails
.Where( cd => cd.EmployeeId == id )
.Sum( cd => cd.Amount );

Entity Framework: One way SaveChanges() works, the other way it does not. Why? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
If I do this, context.SaveChanges() works fine:
List<Messages> DbChangedMessages = context.Messages
.Where(m => m.Status == 0)
.OrderBy(m => m.insertdate)
.Take(maxNumberOfMessages)
.ToList();
//look for messages that point to invalid template
List<Messages> invalidMessages = (from m in DbChangedMessages
join t in context.Templates on m.templateId equals t.id
where m.Status == 0 && t.invalid == true
select m)
.ToList();
if (invalidMessages.Count > 0)
{
//set Status=11 for all messages that have Status=0 and point to an invalid template
invalidMessages.ForEach(m => m.Status = 11);
context.SaveChanges();
}
The new Status 11 is written to the database.
But if I move the first query to a static function returning the List like this, SaveChanges() does not work (the rest is unchanged).
List<Messages> DbChangedMessages = MessageMethods.GetChangedMessages(maxNumberOfMessages);
//look for messages that point to invalid template
List<Messages> invalidMessages = (from m in DbChangedMessages
join t in context.Templates on m.templateId equals t.id
where m.Status == 0 && t.invalid == true
select m)
.ToList();
if (invalidMessages.Count > 0)
{
//set Status=11 for all messages that have Status=0 and point to an invalid template
invalidMessages.ForEach(m => m.Status = 11);
context.SaveChanges();
}
Why?
The change tracking that Entity Framework does is done by the DbContext itself. The MessageMethods.GetChangedMessages method is using a different DbContext than the method that calls it, so the changes aren't tracked properly. You could either pass in the context to the GetChangedMessages method or you could attach the entities to the context as you need to:
invalidMessages.ForEach(m =>
{
m.Status = 11;
context.Messages.Attach(m);
});

Filtering Entity Collections [duplicate]

This question already has answers here:
EF: Include with where clause [duplicate]
(5 answers)
Closed 6 years ago.
I want to be able to filter a child collection and only return items in that child collection which match certain conditions. Here's the code that I have now:
var q = from u in context.DbContext.Users select u;
q = q.Include(u => u.UserRoles.Select(ur => ur.Role))
.Where(u=> u.UserRoles.Any(ur=> ur.EnvironmentId == environmentId)
);
My issue with this code is that this is also returning UserRole objects in the UserRole collection that do not match.
For example, if my environmentId variable has a value of 1, in only want the UserRoles returned in the collection if they have a value of 1 for the EnvironmentId property.
As of right now, it is returning every UserRole regardless of the EnvironmentId value.
Edit
This is not a duplicate question as Gert Arnold has suggested. I do not want to create new or anonymous objects, and the solution i proposed below solves this problem, whereas the article linked to by Gert Arnold does not.
Your Where condition is not applied to the right collection. Here, you are applying the Where to the User collection so that it will only return users that have at least one role where EnvironmentId is 1. What you want to do instead is apply that to your Role collection to only join the ones you want. this doesn't workI believe something like this should work:
q = q.Include(
u => u.UserRoles.Where(ur => ur.EnvironmentId == environmentId)
.Select(ur => ur.Role))
What you can do instead would be to return a new object via a select (I'm getting into unsure territory right now :)
q = q.Select(u =>
new {
User = u,
Roles = u.UserRoles.Where(ur => ur.EnvironmentId == environmentId)
};
Now here comes the weird part... this will return you an anonymous object where the User propertie is your returned user, and Roles, your returned roles. If you wish you create a new class so that you can carry that value around outside of the scope of that block.
new class
public class UserWithRoles
{
Public User User {get; set;}
IEnumarable<Roles> Roles {get; set;}
}
query
q => q.Select(u =>
new UserWithRoles() {
User = u,
Roles = u.UserRoles.Where(ur => ur.EnvironmentId == environmentId)
};
That way you can declare a List<UserWithRoles> UserList and you could do UserList = q.ToList(); This might not be (probably is not) the best way to do it, but it is one way I believe it will work. If anyone is moire knowledgeable than me in LINQ's Include and knows how to make this work better, please post another answer or comment this one, I'd like to know too :)
you might consider just returning a list of userroles and you can select the users from this list if you need the user objects
var roles = from ur in context.DbContext.UsersRoles.Include("User")
where ur.EnvironmentId == environmentId
select ur;
var users = roles.SelectMany(a => a.Users).Distinct();
using the example provided here Filtering Related Entity Collections I came up with what seems to be a clear and elegant solution to the problem. this only loads the items in the collection if they match, and doesn't require creating any anonymous objects. (note: LazyLoading must be turned off for explicit loading to work)
User user;
var data = from u in context.DbContext.Users select u;
user = data.FirstOrDefault();
// load UserRoles and UserRoles.Role
context.Entry(user)
.Collection(u => u.UserRoles)
.Query()
.Include(ur => ur.Role)
.Where(ur => ur.EnvironmentId == environmentId)
.Load()
;

Categories

Resources