LINQ: Problem using DB with relations - c#

I don't know how can I return posts to my view where their tags are equal to those who are passed to controller action.
I think there is some clever and easy way of doing this but I am very new to LINQ and SQL.
Code
// id = tag name, not it's id
public ActionResult Tag(string id)
{
// I get all the PostTags where PostTags.Tag.Name = id
var postTags = _db.PostTags.Where(x => x.Tag.Name == id);
// And what I do now?
}

Using joins in relational data is easier to grasp as a novice using query syntax instead of extension methods. The following is possible with extension methods (like .Join(...), etc), but this is closer to the SQL you might already be used to.
var postTags = from t in _db.Tags
join pt in _db.PostTags on t.ID equals pt.TagID
join p in _db.Posts on pt.PostID equals p.ID
where t.Name == id
select p;

Well to select them you could do something similar to..
var posts = _db.Posts.Where(post => post.PostTags.Any(postTag => postTag.Tag.Name == id));
This will just select all Posts where any of the related PostTags has a Tag with the name passed.

Related

Search method joing multiple tables

I need help with a search method for searching the tables for a matching text.
This works, except that the join needs to be LEFT OUTER JOIN otherwise I dont get any results if the pageId is missing in any of the tables.
This solution takes to long time to run, I would appreciate if someone can help me out with a better solution to handle this task.
public async Task<IEnumerable<Result>> Search(string query)
{
var temp = await (from page in _context.Pages
join pageLocation in _context.PageLocations on page.Id equals pageLocation.PageId
join location in _context.Locations on pageLocation.LocationId equals location.Id
join pageSpecialty in _context.PageSpecialties on page.Id equals pageSpecialty.PageId
join specialty in _context.Specialties on pageSpecialty.SpecialtyId equals specialty.Id
where
page.Name.ToLower().Contains(query)
|| location.Name.ToLower().Contains(query)
|| specialty.Name.ToLower().Contains(query)
select new Result
{
PageId = page.Id,
Name = page.Name,
Presentation = page.Presentation,
Rating = page.Rating
}).ToListAsync();
var results = new List<Result>();
foreach (var t in temp)
{
if (!results.Exists(p => p.PageId == t.PageId))
{
t.Locations = GetLocations(t.PageId);
t.Specialties = GetSpecialties(t.PageId);
results.Add(t);
}
}
return results;
}
Using navigation properties, the query could look like:
var temp = await (from page in _context.Pages
where Name.Contains(query)
|| page.PageLocation.Any(pl => pl.Location.Name.Contains(query))
|| page.PageSpecialties.Any(pl => pl.Specialty.Name.Contains(query))
select new Result
{
PageId = page.Id,
Name = page.Name,
Presentation = page.Presentation,
Rating = page.Rating,
Locations = page.PageLocation.Select(pl => pl.Location),
Specialties = page.PageSpecialties.Select(pl => pl.Specialty)
}).ToListAsync();
This has several benefits:
By the absence of joins, The query returns unique Result objects right away, so you don't need to deduplicate them afterwards.
The locations and specialties are loaded in the same query instead of two queries per Result (aka n+1 problem).
(Likely) ToLower is removed because the search is probably not case sensitive anyway. The query is executed as SQL and most of the times, SQL databases have case-insensitive collations. Removing ToLower makes the query sargable again.

Linq Join issue in C#

I am so very new to c# and am learning as I go...
I have two tables, Blog and BlogCategories. The Blog table has an id that references the category in BlogCategories.
I am having an issue with the syntax on the join:
var categories = new List<BlogCategory>();
if (model.BlogCategoryId.HasValue)
{
var query =
from category in categories
join blog in model on category.Id equals model.BlogCategoryId
select new { BlogCategory = category.Name };
}
The issue is that it doesn't like the join and get :
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'
Any help would be greatly appreciated... PHP is not a big boy language, this is.
Quick table structure
BlogPosts table:
Id,
BlogCategory,
Title,
Content
BlogCategories:
Id,
CategoryName
EDIT:
Solution seems like it would work but error somewhere:
var blogPosts = PopulateBlogPosts();
if (model.BlogCategoryId.HasValue)
{
var blogPostCategories = PopulateBlogCategories();
blogPosts = blogPostCategories.Where(c => c.Id == model.BlogCategoryId).Single();
}
Assuming categories is an IEnumerable<BlogCategory>.
var category = categories.Single(c => c.Id == model.BlogCategoryId);
This Single extension method asserts you're only expecting one object to match, and it allows you to filter. It will throw an exception if there is anything other than one category that meets the filter criteria. It's a shorter form of this:
var category = categories.Where(c => c.Id == model.BlogCategoryId).Single();

How to use Join and Where to return an IList?

How can I achieve a join and a where in C# MVC using something like Linq or EF Join?
This is the equivalent SQL I am trying to achieve.
select * from promotion P
JOIN PromotionsClaimed PC
on PC.PromotionId = P.objectid
where PC.userId = #USERID
This method should return a list of the promotions for the user. First, I get a list of all of the promotions, then I get the composite list of claimed promotions for the user. This is what I have so far.
public IList<Promotion> GetRewardsForUser(string userId)
{
//a list of all available promotions
IList<Promotion> promos = _promotionLogic.Retrieve();
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic.RetrieveByCriteria(t => t.userId == userId);
//should return a list of the Promotion name and code for the rewards claimed by user, but a complete list of Promotion entities would be fine
var selectedPromos =
from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
select new { PromoName = promo.Name, PromoCode = promo.Code };
return selectedPromos;
}
I realize there are a lot of problems here. I'm trying to learn Linq and Entity Framework, but I don't know how to add the where clause to an IList or if there is an easier way to accomplish this.
It seems to me like there would be a way to filter the promotion list where it contains the Promotion.objectId in the promosClaimed list, but I don't know the syntax.
public IList<Promotion> GetRewardsForUser(string userId)
{
//a list of all available promotions
IList<Promotion> promos = _promotionLogic.Retrieve();
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic.RetrieveByCriteria(t => t.userId == userId);
//should return a list of the Promotion name and code for the rewards claimed by user, but a complete list of Promotion entities would be fine
var selectedPromos =
(from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
select new { PromoName = promo.Name, PromoCode = promo.Code }).ToList();
return selectedPromos;
}
If I understood your question correctly, you could do something like this:
public IList<Promotion> GetRewardsForUser(string userId)
{
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic
.RetrieveByCriteria(t => t.userId == userId);
var promotionIds = promosClaimed.Select(p => p.PromotionId).ToList();
IList<Promotion> promos = _promotionLogic.Retrieve()
.Where(p => promotionIds.Contains(p.objectId))
.Select(p => new { PromoName = p.Name, PromoCode = p.Code });
return selectedPromos;
}
The claimed promotions should be already filtered by a user so this should possibly work.
First of all, you're using entity framework? Or are you just trying to do join of two collections?
Because if you are using EF, you're thinking the wrong way. In the entity the proper way is to use include, for example:
public DbSet<promotion > promotion { get; set; }
public DbSet<PromotionsClaimed> PromotionsClaimed{ get; set; }
Context.promotion.Include(o => o.PromotionsClaimed).FirstOrDefault(s => s.Id == USERID);
If you need only join two collection using linq, you can do that.
var userId = 1;
var test =
(
from p in promos
join pc in promosClaimed on p.objectid equals pc.PromotionId
where pc.userId == userId
select p
).ToList();
Have you tried just to add your condition to your code? Like:
var selectedPromos =
from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
where promosClaimed.UserId == userId
select new { PromoName = promo.Name, PromoCode = promo.Code };
That should work or I just didn't understand you

best way to select...where in using linq

There are three tables in the database that are relevant. Advocate, Vendor, and Advocate_Vendor.
The Advocate_Vendor table being the many to many link, has a vendorId and an advocateId.
My end goal is to get back a List<Advocate> object...a collection of advocates that belong to one Vendor. I wrote this:
var list = new List<Advocate>();
foreach (var vendorAdvocates in db.Advocate_Vendors)
{
if (vendorAdvocates.VendorId == vendorId)
{
list.Add(db.Advocates.SingleOrDefault(a => a.AdvocateId == vendorAdvocates.AdvocateId));
}
}
And then this:
var list = (from vendorAdvocates in db.Advocate_Vendors
where vendorAdvocates.VendorId == vendorId
select db.Advocates.SingleOrDefault(a =>
a.AdvocateId == vendorAdvocates.AdvocateId)).ToList();
Is this the best way? Seems wrong, like maybe there could be a more streamlined way to do this using a 'contains' keyword or something that looks a bit more readable...get all the vendor's advocates
thanks
Using a join between Advocate_Vendors and Advocates would be the right way of doing it.
var list = (from vendorAdvocates in db.Advocate_Vendors
join advocates in db.Advocates
on vendorAdvocates.AdvocateId equals advocates.AdvocateId
where vendorAdvocates.VendorId == vendorId
select advocates).ToList();
var list = (from vendorAdvocates in db.Advocate_Vendors
from advocate in db.Advocates
where vendorAdvocates.VendorId == vendorId &&
vendorAdvocates.AdvocateId = advocate.Id
select advocate)
.ToList();
If you set up your foreign keys and navigation properties properly, it should be possible to write this way, or something like it:
var list = (from vendorAdvocates in db.Advocate_Vendors
where vendorAdvocates.VendorId == vendorId
select db.Advocate).ToList();

Linq to Sql - How to get data from second level table

I'm newbie to linq to sql, just trying to understand what type of queries I can handle with linq.
Here is my database scheme,
I want to get all customers of a specific user and this is what I've done,
var userId = 4;
var companies = from c in db.Company
where c.UserId == userId
select c.Id;
var costumers = from c in db.Customers
where companies.Contains(c.CompanyId)
select c;
I'm just wondering whether it's a nice approach and is there any better method to handle this type of queries?
Use can also get customers by this way also:
var result = db.Customers.Join(
db.Company, customer => customer.CompanyId, comp => comp.Id, (customer, comp)
=> new { customer, comp }).Where(#t => #t.comp.UserId == 4)
.Select(#t => #t.customer);
You can also keep it simple like this.
select * from db.Customers Cus
inner join db.company Com on Com.Id = Cus.CompanyId
where Com.UserId= userId
Contains is the equivalent of IN in SQL and your Linq statement will be translated to a SQL statement. So I can't really see another way that will give you better performance with Linq. If you want to use less code you can maybe try the following instead:
var companies = db.Companies.Where(x=> x.UserId == userid).Select(x=>x.Id);
var customers = db.Customers.Where(x=> companies.Contains(x.CompanyId));

Categories

Resources