How to specify a condition in an Entity Framework join? - asp.net-mvc

I have a Blogs table related to BlogComments table with a FK.
I need to get, through Linq, all the BlogComments items that match a certain flag
If i do:
db.Blogs.Where(b => b.BlogComments.Where(bc=>bc.Where(bc.Flag1==true));
I get "Cannot implicity convert type IEnumerable to bool"
Which is the best way to solve this problem?

Because this expression:
b.BlogComments.Where(...)
returns an IEnumerable (of BlogComments), but you are then passing it into this method:
db.Blogs.Where(...)
which expects a function that returns a bool, not an IEnumerable.
You probably need something like this:
var blogId = 5;
db.BlogComments.Where(bc => bc.BlogId == blogId && bc.Flag1 == true)
If you need to select comments from multiple blogs, then you could try using Contains:
var blogIds = new [] {1,2,3,4,5};
db.BlogComments.Where(bc => blogIds.Contains(bc.BlogId) && bc.Flag1 == true)
If you want to place criteria on the set of blogs, as well as the comments, then you could do this in one query using a join:
var query = from b in db.Blogs
join c in db.BlogComments on c.Blog equals b
where b.SomeField == "some value"
&& c.Flag1 == true
select c;

You could write it in LINQ form.
var blogs = from b in db.Blogs
join c in db.BlogComments
on b.BlogId equals c.BlogId
where c.Flag1
select b;
If you have a composite key you can write
on new { A = b.BlogKey1, B = b.BlogKey2 }
equals new { A = c.CommentKey1, B = c.CommentKey2 }

If it were me, I would just have another DbSet in your DbContext.
DbSet<BlogComment> BlogComments
and just search through there without going through Blogs.
db.BlogComments.Where(bc => bc.Flag1 == true);
If anyone knows if there's anything wrong in doing so, then I'm all ears :)

Related

LINQ query with omitted user input

so I have a form with several fields which are criteria for searching in a database.
I want to formulate a query using LINQ like so:
var Coll = (from obj in table where value1 = criteria1 && value2 = criteria2...)
and so on.
My problem is, I don't want to write it using If statements to check if every field has been filled in, nor do I want to make separate methods for the various search cases (criteria 1 and criteria 5 input; criteria 2 and criteria 3 input ... etc.)
So my question is: How can I achieve this without writing an excessive amount of code? If I just write in the query with comparison, will it screw up the return values if the user inputs only SOME values?
Thanks for your help.
Yes, it will screw up.
I would go with the ifs, I don't see what's wrong with them:
var query = table;
if(criteria1 != null)
query = query.Where(x => x.Value1 == criteria1);
if(criteria2 != null)
query = query.Where(x => x.Value2 == criteria2);
If you have a lot of criteria you could use expressions, a dictionary and a loop to cut down on the repetitive code.
In an ASP.NET MVC app, chances are your user input is coming from a form which is being POSTed to your server. In that case, you can make use of strongly-typed views, using a viewmodel with [Required] on the criteria that MUST be provided. Then you wrap your method in if (ModelState.IsValid) { ... } and you've excluded all the cases where the user hasn't given you something they need.
Beyond that, if you can collect your criteria into a list, you can filter it. So, you could do something like this:
filterBy = userValues.Where(v => v != null);
var Coll = (from obj in table where filterBy.Contains(value1) select obj);
You can make this more complex by having a Dictionary (or Lookup for non-unique keys) that contains a user-entered value along with some label (an enum, perhaps) that tells you which field they're filtering by, and then you can group them by that label to separate out the filters for each field, and then filter as above. You could even have a custom SearchFilter object that contains other info, so you can have filters with AND, NOT and OR conditions...
Failing that, you can remember that until you trigger evaluation of an IQueryable, it doesn't hit the database, so you can just do this:
var Coll = (from obj in table where value1 == requiredCriteria select obj);
if(criteria1 != null)
{
query = query.Where(x => x.Value1 == criteria1);
}
//etc...
if(criteria5 != null)
{
query = query.Where(x => x.Value5 == criteria5);
}
return query.ToList();
That first line applies any criteria that MUST be there; if there aren't any mandatory ones then it could just be var Coll = table;.
That will add any criteria that are provided will be applied, any that aren't will be ignored, you catch all the possible combinations, and only one query is made at the end when you .ToList() it.
As I understand of your question you want to centralize multiple if for the sake of readability; if I were right the following would be one of some possible solutions
Func<object, object, bool> CheckValueWithAnd = (x, y) => x == null ? true : x==y;
var query = from obj in table
where CheckValue(obj.value1, criteria1) &&
CheckValue(obj.value2, criteria2) &&
...
select obj;
It ls flexible because in different situations or scenarios you can change the function in the way that fulfill your expectation and you do not need to have multiple if.
If you want to use OR operand in your expression you need to have second function
Func<object, object, bool> CheckValueWithOr = (x, y) => x == null ? false : x==y;

Need help to build an EF query with LEFT JOIN

I can't find the correct way to build an EF (4.1) query that will return the same result as this SQL containing a LEFT JOIN:
SELECT
s.id_service,
s.description,
x.id_service as isDisponible
FROM
role.service_disponible s
LEFT JOIN
role.service_disponible_x_ue x
ON s.id_service = x.id_service AND x.id_ue = 1 and flg_actif = '1'
In fact I'm just trying to obtain the complete list of services disponible (ServiceDisponible) adding a field that tell me if service is disponible for a specific entity (filtered with the id_ue) which information come from a many to many related table (ServiceDisponibleXUe).
My model is:
Ideally, I would like this query to return this viewModel object what is basically my serviceDisponible domain with one more field indicating the disponibility of the service.
public ServiceDisponibleViewModel(ServiceDisponible ServiceDisponible, bool isDisponible)
{
this.serviceDisponible = serviceDisponible;
this.isDisponible = isDisponible;
}
What I have so far is this query but the syntax is invalid:
services = context.ServiceDisponible
.Select(a => new ServiceDisponibleViewModel
{
c => new ServiceDisponible
{
id_service = a.id_service,
description = a.description
},
isDisponible = a.ServiceDisponibleXUe
.Any(b => b.flg_actif && b.id_ue == idUe)
}).ToList();
Try this:
ServiceDisponibleViewModel services =
from sd in context.ServiceDisponible
from sdx in context.ServiceDisponibleXUe
.Where(x => x.id_ue == 1 && flg_actif == '1' && x.id_service == sd.id_service)
.DefaultIfEmpty()
select new ServiceDisponibleViewModel(
new ServiceDisponible
{
id_service = sd.id_service,
description = sd.description
},
sdx.id_service
);
Having SQL as example often makes one jump to a join in linq. But using navigation properties produces much more succinct syntax:
from sd in context.ServiceDisponible
from sdx in sd.ServiceDisponibleXUes.Where(x => x.id_ue == 1
&& x.flg_actif == "1")
.DefaultIfEmpty()
select new
{ sd.id_service,
sd.description,
isDisponible = sdx.id_service
};
(I couldn't help using the plural form of ServiceDisponibleXUe which imo is more clear).

Getting a list of distinct entities projected into a new type with extra field for the count

I'm designing an interface where the user can join a publicaiton to a keyword, and when they do, I want to suggest other keywords that commonly occur in tandem with the selected keyword. The trick is getting the frequency of correlation alongside the properties of the suggested keywords.
The Keyword type (EF) has these fields:
int Id
string Text
string UrlString
...and a many-to-many relation to a Publications entity-set.
I'm almost there. With :
var overlappedKeywords =
selectedKeyword.Publications.SelectMany(p => p.Keywords).ToList();
Here I get something very useful: a flattened list of keywords, each duplicated in the list however many times it appears in tandem with selectedKeyword.
The remaining Challenge:
So I want to get a count of the number of times each keyword appears in this list, and project the distinct keyword entities onto a new type, called KeywordCounts, having the same fields as Keyword but with one extra field: int PublicationsCount, into which I will populate the count of each Keyword within overlappedKeywords. How can I do this??
So far I've tried 2 approaches:
var keywordCounts = overlappingKeywords
.Select(oc => new KeywordCount
{
KeywordId = oc.Id,
Text = oc.Text,
UrlString = oc.UrlString,
PublicationsCount = overlappingKeywords.Count(ok2 => ok2.Id == oc.Id)
})
.Distinct();
...PublicationsCount is getting populated correctly, but Distinct isn't working here. (must I create an EqualityComarer for this? Why doesn't the default EqualityComarer work?)
var keywordCounts = overlappingKeywords
.GroupBy(o => o.Id)
.Select(c => new KeywordCount
{
Id = ???
Text = ???
UrlString = ???
PublicationsCount = ???
})
I'm not very clear on GroupBy. I don't seem to have any access to 'o' in the Select, and c isn't comping up with any properties of Keyword
UPDATE
My first approach would work with a simple EqualityComparer passed into .Distinct() :
class KeywordEqualityComparer : IEqualityComparer<KeywordCount>
{
public bool Equals(KeywordCount k1, KeywordCount k2)
{
return k1.KeywordId== k2.KeywordId;
}
public int GetHashCode(KeywordCount k)
{
return k.KeywordId.GetHashCode();
}
}
...but Slauma's answer is preferable (and accepted) because it does not require this. I'm still stumped as to what the default EqualityComparer would be for an EF entity instance -- wouldn't it just compare based on primary ids, as I did above here?
You second try is the better approach. I think the complete code would be:
var keywordCounts = overlappingKeywords
.GroupBy(o => o.Id)
.Select(c => new KeywordCount
{
Id = c.Key,
Text = c.Select(x => x.Text).FirstOrDefault(),
UrlString = c.Select(x => x.UrlString).FirstOrDefault(),
PublicationsCount = c.Count()
})
.ToList();
This is LINQ to Objects, I guess, because there doesn't seem to be a EF context involved but an object overlappingKeywords, so the grouping happens in memory, not in the database.

can anyone advice with getting count for amount of products in category for specified results with linq

I have created a set of search results, and I wish to create a filter of available cats, with the number of results within that filter. however I get the most strangest error when trying to do this.
Unable to create a constant value of type 'NAMESPACE.Models.Products'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
this is the code i have tried:
var cats = (from p in ctx1.SubCategories
where myCats.Contains(p.subCategoryId) && p.enabled
select new
AvailableSubCats
{
CategoryName = p.subCategoryName,
Id = p.subCategoryId,
TotalItems = model.Count(x => x.subCategoryId == p.subCategoryId)
}).Distinct();
Products is the object that is called model on the line of totalItems.
I have also tried this:
var cats = from c in ctx1.SubCategories
join p in model on c.subCategoryId equals p.subCategorySubId
group p by c.subCategoryName
into g
select new
AvailableSubCats
{
CategoryName = g.Key,
Id = 0,
TotalItems = g.Count()
};
with the same error, and dont like this because i dont know how to get the name of the category and its ID.
help much appreciated.
thanks
p.s I am using Entity framework 4.1, .net 4 and MVC 3, mysql
in short i am trying to run this in linq, but were the the products side is already a result
select c.*, (select count(productId) from Products where Products.subCategoryId = c.subCategoryId) as counter from SubCategories c
You could try turning your list of products into a list of subCategoryId's so EF can understand it. Something like:
var subCategoryIds = model.Select(m => m.subCategoryId);
var cats = (from p in ctx1.SubCategories
ctx1.SubCategories
where myCats.Contains(p.subCategoryId) && p.enabled
select new
AvailableSubCats
{
CategoryName = p.subCategoryName,
Id = p.subCategoryId,
TotalItems = subCategoryIds.Count(x => x == p.subCategoryId)
}).Distinct();

LINQ Query to Produce Non-Empty Nested Menu?

To see my problem in action, visit www.apoads.com and hover over the "Local Businesses" menu item. It's a series of nested unordered lists generated from a db call. Go ahead and click on a few of the items underneath "Local Businesses", you'll see that most of the categories are empty (the site is very new).
Problem:
I only want to show the categories that actually contain a local business. Here's what my category schema looks like:
int BizCatID - PK,Identity (used in FK relation to the table named Biz)
int? ParentID - BizCatID of this rows parentID, null means no parent
nvarchar Name - name of the category
nvarchar Caption - quick description of the category
What I've tried:
I've tried to update my LINQ query like so:
from c in db.BizCategories where c.ParentID != null && c.Bizs.Count() > 0 select c;
That obviously won't work, cause I'll need the parent category to show if the child category contains a business. So I tried this:
from c in db.BizCategories where c.Bizs.Count() > 0 select c;
This doesn't work either, as parent categories will never have any businesses under them. So it seems like I'll need to do some sort of inverse recursion, but I'm not sure how to do that.
Or, perhaps I'm making things to hard for myself and I need to change my db schemas?
create classes to hold your information like so
public class BusinessCat
{
public string Name{get;set;}
public string Caption{get;set;}
public List<'dunno datatype'> Children{get;set;};
}
var results = from c in BizCategories
where c.ParentID == null
select new BusinessCat{
Name = c.Name,
Caption = c.Caption,
Children = (from d in BizCategories
where d.ParentID == c.BizCatID && d.Bizs.Count() > 0 select d).ToList()
}
and with this you have a list full of Children and you can simply iterate through it with a foreach.
You can also do .Join() extension method for
where d.ParentID == c.BizCatID && d.Bizs.Count()
I'm not sure about the recursion bit but this might get you started. Is a two-level query.
I'm guessing you want to support any number of levels.
var allcats = (from c in db. BizCategories
select c).ToList(); // This will retrieve them all from the database.
var twoLevels = from c in allcats
where c.ParentID == null
select new {
Name = c.Name, Caption = c.Caption,
Children =
from d in allcats
where d.ParentID == c.BizCatID
select d
};
A query like this is getting close to what I need:
from c in BizCategories
where c.ParentID == null
select new {
Name = c.Name, Caption = c.Caption,
Children =
from d in BizCategories
where d.ParentID == c.BizCatID && d.Bizs.Count() > 0
select d
}
I should be able run another LINQ query over these results and pull out exactly what I need. Not at a computer where I can test that now, but will reply once I can. Thanks!

Resources