Is it possible to compare with a number in "Where"? - entity-framework

I am new in Entity Framework and I have a little question.
Is it possible to compare in "Where" with a number.
I will add code example.
var source = db.Book.Where(book => book.Count > 0);
So, I need to get all book items where count more than zero.
Thanks in advance for your help!
Here is the structure of database

I'm not sure why you are trying to query the entire table to check if there is any record, yet you can do something like:
var query = context.Books.Any() ? context.Books.ToList() : null;
if (query is not null)
{
query.ForEach(b =>
{
Console.WriteLine($"ID:{b.Id}, Title:{b.Title}");
});
}
However, if you are trying to find out if there is duplication available, you can use the following query:
var query = context.Books.FirstOrDefault(b => b.Title == "some title")
is not null ? context.Books.ToList() : null;
if (query is not null)
{
query.ForEach(b =>
{
Console.WriteLine($"ID:{b.Id}, Title:{b.Title}");
});
}
By the way, I don't think it is a good idea to have such a scenario.

Related

How to concatenate column data and return in linq select

I have a table where it has the columns like IsAgreed, IsOther, IsEquipped. On the UI I am showing 3 check boxes where one can select single or multiple check boxes. The data is getting saved to db as expected. Now I am trying to select the data in entity framework as follows
from tbl context.TableNames select new {
Conitions= tbl.IsOther ? tbl.OtherText : tbl.IsAgreed ? "Agreed :
tbl.IsEquipped? "Equipped" : "" }
Which is giving only one selection when the multiple selection are made. I would like to concat and result the data so that it can be
OtherText, Agreed, Equipped
OtherText, Equipped
Agreed, Equipped
Is it possible to concatenate and give the expected output
You can create an array of strings based on conditions, after it can be formatted as desired. Please pay attention to the comment i wrote in the code sample.
var conditions = context.TableNames.Select(tbl => new
{
tbl.IsOther,
tbl.IsAgreed,
tbl.IsEquipped
})
.AsEnumerable() //Should be used with caution. Because it will load each record to memory. It also switches "LINQ to Entities" to "LINQ to Objects", so we can use string.Join.
.Select(c => new
{
Conditions = string.Join(", ", new string[] { c.IsOther ? "OtherText" : "", c.IsAgreed ? "Agreed" : "", c.IsEquipped ? "Equipped" : "" }.Where(s => !string.IsNullOrEmpty(s)))
});

How to Data Fetch using Entity Framework in dotnet core

I have a table called "UserAnswers".below screenshot contains table data
I want to get data by surveyId and group by CreatedBy column.
for an example
There is a user called "amara#gmail.com".this user contains 4 records for a SurveyId.
I want to get this like below
Answers : [
{"2"},
{"1","0","1","1"},
{"1","2","4","3"},
{"Blue"}]
But my code returns this array for every rows.I meant duplicate records returning.
Here is my code
var qstns = await (from uans in _context.UserAnswers
where uans.SurveyId == id
select new UserAnswersReturnDto
{
UserEmail = uans.CreatedBy,
Qustns = (from ans in _context.UserAnswers
where ans.CreatedBy == uans.CreatedBy
select new UserAnswersSet
{
QNo = ans.QNo,
Ansrs = JsonConvert.DeserializeObject<JArray>(string.IsNullOrEmpty(ans.Answers) ? "[]" : ans.Answers)
}).ToArray()
}).ToListAsync();
So how to solve this issue.I opened many questions for this problem,but no one answered.Please help me.Thanks in advanced
You need to actually group your data before returning:
I used LINQ Lambda notation, but it should be quite easy to translate back to query if you're so inclined:
var qstns = _context.UserAnswers.Where(uans => uans.SurveyId == id)
.GroupBy(uans => uans.CreatedBy)
.Select(grans => new UserAnswersReturnDto {
UserEmail = grans.Key,
Qustions = grans.Select(ans => new UserAnswersSet() {
QNo = ans.QNo,
Ansrs = ans.Answers
}).ToList()
} ).ToList();
I didn't have time to double-check this, but I hope it serves as a guide to help you solve your issue!
There is no group by statement in your linq query.

Brute force collection in Entity Framework

Good day to all!
There is such a code
var userRoles = context.UserRoles.Where(ur => ur.UserId == id);
if (userRoles.Any())
{
context.UserRoles.RemoveRange(userRoles);
}
var userCars = context.UserCars.Where(uc => uc.UserId == id);
It is necessary to get rid of the extra SELECT code before deleting:
context.UserRoles.RemoveRange (userRoles);
How to do it better?
If RemoveRange loops through the incoming collection, then for each record in the UserCars collection, a separate query will be executed into the database, which is bad. The sample should be within the same request:
var userCars = context.UserCars.Where (uc => uc.UserId == id);
How to do it better?
Sincerely, Mikhail
if i understand right you want to perform the select and the delete in one query
i think this should help you Entity Framework Plus
Batch Delete

How to do a search with EF CodeFirst

Currently, to do a search using EF CodeFirst and a repository pattern, based on user input to multiple text boxes on an mvc search view/page, I do something like the following:
public PagedList<Entity1> PlayerUserSearch(Entity1SearchParameters searchParameters, int? pageSize, int? startEntity, Func<Entity1, object> sortOrder, bool sortDesc)
{
IQueryable<Entity1> query = from entities in this.DataContext.Entity1s.Include("Entity2List")
where entities.Entity2List.Any()
select entities;
if (searchParameters.Entity2PrimaryKeyId.HasValue)
query = query.Where(e => e.Id == searchParameters.Entity2PrimaryKeyId.Value);
if (searchParameters.HasStats.HasValue)
{
if (searchParameters.HasStats.Value)
query = query.Where(u => u.Entity2List.Any(e => e.Stat != null));
else
query = query.Where(u => u.Entity2List.Any(e => e.Stat == null));
}
if (searchParameters.Entity2OtherField.HasValue)
query = query.Where(u => u.Entity2List.Any(e => e.Event.Entity2OtherField == searchParameters.Entity2OtherField));
if (searchParameters.Entity2OtherField2.HasValue)
query = query.Where(u => u.Entity2List.Any(e => e.Event.Entity2OtherField2 == searchParameters.Entity2OtherField2));
if (searchParameters.Active.HasValue)
query = query.Where(e => e.Active == searchParameters.Active.Value);
return this.GetPageByStartEntity(pageSize.Value, startEntity.Value, query, sortOrder, sortDesc);
}
The problem with this is that for every time I add on another where that checks the child of Entity1 (Entity2) for a certain field, it takes on a new " AND EXISTS" clause to the sql statement generated, so that it is doing an exists and checking table Entity2 all over again for every different field checked, rather than doing a single EXISTS on Entity in the query, and checking all fields I tacked on to the query (i.e. EntityOtherField1 and EntityOtherField2). I haven't been able to find a better way to do a search based on user inputs than constantly checking for the input being there (add to the search parameters)) and then tacking on a new where to the current query. Can anyone tell me if there is a better way to do this? Thanks!
I think what you're looking for is using Specification Pattern.
var spec = new specification<Entity2>(s => true);
if (searchParameters.HasStats.Value)
{
spec = spec.And(e => e.Stat != null);
}
if (searchParameters.Entity2OtherField2.HasValue)
{
spec = spec.And(e => e.Event.Entity2OtherField2 == searchParameters.Entity2OtherField2);
}
query = query.Where(u => u.Entity2List.Any(spec));
Or I believe you can make it more standard by separating the filter logic and using spec.IsStatisfiedBy method.
A good framework for repository/specification implemetation on Entity Framework can be found here.

Entity Framework Include with condition

I need to filter a dealer based on id and the uncomplete checkins
Initially, it returned the dealer based only on id:
// TODO: limit checkins to those that are not complete
return this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
.FirstOrDefault();
As you can see the requirement is to limit the checkins.
Here's what I did:
var query = from d in this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
select new
{
Dealer = d,
Groups = from g in d.Groups
select new
{
Items = from i in g.Items
select new
{
Group = i.Group,
Observations = i.Observations,
Recommendations = i.Recommendations
}
},
Checkins = from c in d.Checkins
where c.Complete == true
select new
{
Inspections = from i in c.Inspections
select new
{
InspectionItems = i.InspectionItems
}
}
};
var dealer = query.ToArray().Select(o => o.Dealer).First();
return dealer;
It works.
However, I am not convinced I am doing the right thing.
What is the best way to accomplish what I did? A stored procedure maybe?
I am not sure I even have to use Include clause anymore
Thank you.
If you want to load filtered relations with single query you indeed have to execute such projection but you don't need those calls to Include. Once you are building projections includes are not use - you have returned data under your control.
Stored procedure will help you only if you fall back to plain ADO.NET because stored procedures executed through Entity framework are not able to fill related entities (only flattened structures).
Automatic fixupu mentioned by #Andreas requires multiple database queries and as I know it works only if lazy loading is disabled because proxied object somehow doesn't have information about fixup and it still has its internal flags for each relation as not loaded so when you access them for the first time they still execute additional query.
Maybe you can make use of the relation fixup mechanism in the EF ObjectContexts. When you do multiple queries in the same context for entities, that are related by associations, these are resolved.
Assuming your association between Dealers and Checkins is 1:n with navigation properties on each side, you could do like:
var dealer = yourContext.Dealers
.Where(p => p.DealerId == id)
.FirstOrDefault();
if(dealer != null)
{
yourContext.Checkins
.Where(c => c.Complete && c.DealerId == dealer.DealerId)
.ToList();
I have not tested this by now, but since EF recognises that the Checkins, it inserts into the context by the second query belong to the dealer from the first query, corresponding references are created.
#Andreas H:
Awesome, thank you a lot.
I had to adjust your suggestion like this and it worked:
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Include("Inspections")
.Include("Inspections.InspectionItems")
.Where(c => !c.Complete && c.Dealer.DealerId == dealer.DealerId)
.ToList();
}
return dealer;
I still have to use the Include otherwise it won't return the referenced entities.
Note also that Dealer.Groups are unrelated to the Dealer.Checkins.
So if there's no checkins satisfying the condition, Groups still need to be returned.
It's interesting to note that at first, I put the two include for checkins to the dealer
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Where(c => c.Complete && c.DealerId == id)
.ToList();
}
return dealer;
but it returned all the Checkins including those which are not complete.
I don't understand exactly why the latter doesn't work but the former does, how are the entities are resolved. I somehow can intuit that the former returns all data.
Your accepted solution will generate multiple database queries. As Ladislav Mrnka said a projection is the only way to pull your result with one query. The maintance of your code indeed hard. Maybe you could use an IQueryable-Extension that builds the projection dynamically and keep your code clean:
var query = this.ObjectContext.Dealers.SelectIncluding( new List<Expression<Func<T,object>>>>(){
x => x.Groups,
x => x.Groups.Select(y => y.Items),
x => x.Groups.Select(y => y.Items.Select(z => z.Observations)),
x => x.Groups.Select(y => y.Items.Select(z => z.Recommendations)),
x => x.Checkins.Where(y => y.Complete==true),
x => x.Checkins.Select(y => y.Inspections),
x => x.Checkins.Select(y => y.Inspections.Select(z => z.InspectionItems))
});
var dealer = query.First();
return dealer;
You can find the extension at thiscode/DynamicSelectExtensions on github