Obtain more information from the user via Inner join Linq - c#

I supposed in the process developed is such that it must show all the movies that are into film tablen and showing off, but this is how I have tried to do this:
it must find out which genres have in users tablen where after to show the users who like the first.
//As I said, I have a session at the top of the code.
int brugerid = Convert.ToInt16(Session["id"]);
var result = (from f in db.films
//it must find out which genres have in users tablen where after to show the users who like the first.
//brugere are users
//gener It is the genes users like.
join usersgenerId in brugere.Fk_generId on gener.generId equals usersgenerId.BrugereId
select new
{
image_navn = ((f.imgs.FirstOrDefault(i => i.feature == true)).navn == null ? "default.png" : (f.imgs.FirstOrDefault(i => i.feature == true)).navn),
image_feature = f.imgs.Where(A => A.feature == true),
film_navn = f.navn,
film_id = f.filmId,
film_tekst = f.tekst,
film_gener = f.gener.navn
}).ToList();
RepeaterFilmList.DataSource = result;
RepeaterFilmList.DataBind();
Table information
Brugere the name
id = BrugereId
Fk_generId belonging to the genes that user has selected.
and many other
Gener is the name
has generId as id

As mentioned in the comment, the question really is: show all movies that is in the same genre that the user preferred and then show everything else.
Although the following approach might not be db efficient (too lazy to create the db for this, so I am simulating everything in memory and using Linq to Object to solve the issue), it can certainly be resolved by the following steps:
Get the recommendation (matching the user's movie genre preference) like so:
var recommendation =
from f in films
from ug in userGenres
where ug.UserId == user.Id && ug.GenreId == f.GenreId
select f;
Now that we know what the user preferred, we can further filter this to just the preferred films' Id... and use that to get the rest of the unpreferred films (basically anything not matching the preferred film Ids):
var recommendedFilmIds = recommendation.Select(f => f.Id);
var everythingElse =
from f in films
where !recommendedFilmIds.Contains(f.Id)
select f;
Finally, join them together using Union and injecting the nessary fields for display purpose like Genre.Name, etc. like so:
var filmList = recommendation.Union(everythingElse).Select(f => new {
f.Id,
f.Title,
Genre = genres.Where(g => g.Id == f.GenreId).Select(g => g.Name).First()
});
And there you have it, the combined list will now contains both preferred films first (at top), followed by unpreferred films afterward.
The simulated tables are as follows: films which contains its own Id and genreId and userGenres which contains many to many relationship between user and genre and a particular user object which contains the user id.
An example of this can be found at: https://dotnetfiddle.net/Skuq3o
If you use EF, and you have a navigation property to genre table and you want to include those table as part of the query, use .Include(x => x.genre) or whatever you call your genre table after from f in films to avoid n+1 select if you wish to include the genre info in the final select clause.

Related

Linq GroupBy Clause not including items with zero count

I have a query below which is supposed to group the result by Id, EntityName, DocType, Jurisdiction. For each group the query also returns the ProductList items.
At the moment if the group contains one or more than one product, Then i can see the result giving out a group with a combination of Id,EntityName,DocType,Jurisdiction and ProductList, However if the result doesnt contain products for a particular group i do not see the group at all. What i would like to do is show the groups even if does not have any products in its group. So if the count of ProductList is zero, i would like to set
ProductList= new List NettingAgreementProductDto. Any input would be highly appreciated.
var result = from nae in nettingAgreementEntities.Result
join no in nettingOpinions.Result
on nae.EntityId equals no.EntityId
join np in nettingProducts.Result
on no.ProductId equals np.Id
group np by new
{ nae.EntityId,
nae.EntityName,
nae.DocType,
nae.Jurisdiction
} into g
select new NettingAgreementEntityDto
{
Id = g.Key.EntityId,
EntityName = g.Key.EntityName,
DocType = g.Key.DocType,
Jurisdiction = g.Key.Jurisdiction,
ProductList = g.Select(x => new
NettingAgreementProductDto
{
Id = x.Id,
Name = x.Name
}).ToList()
};
To recap from the comments, currently your query is using Inner Join for associating NettingAgreementEntity with NettingAgreementProducts. This not only multiplies the result set (and thus requires you to use GroupBy after), but also filters out the NettingAgreementEntity without NettingAgreementProducts.
You can achieve the goal by switching to Group Join (or Left Outer Join + GroupBy).
But why entering all these complications. EF navigation properties allow you to almost forget about manual joins, and also allow you to easily see the multiplicity, thus whether you need to group the result or not.
So what I would suggest is to add the currently missing collection navigation property to your NettingAgreementEntity class:
public class NettingAgreementEntity
{
// ...
public virtual ICollection<NettingOpinion> Opinions { get; set; }
}
Optionally do the same for NettingAgreementProduct in case in the future you need something similar for products (it's a many-to-many relationship and should be able to be queried from both sides).
Also I would rename the NettingOpinion class navigation properties NettingAgreementProductNavigation and NettingAgreementEntityNavigation to something shorter, for instance Product and Entity. These names (as well as the names of the collection navigation properties) do not affect the database schema, but IMHO provide better readability.
Once you have that, you'll see that the desired LINQ query is a matter of simple Selects which convert entity class to DTO and let EF query translator produce the necessary joins for you:
var result = db.Set<NettingAgreementEntity>()
.Selec(nae => new NettingAgreementEntityDto
{
Id = nae.EntityId,
EntityName = nae.EntityName,
DocType = nae.DocType,
Jurisdiction = nae.Jurisdiction,
ProductList = nae.Opinions
.Select(no => new NettingAgreementProductDto
{
no.Product.Id,
no.Product.Name,
}).ToList(),
});

Using query results with joined parameters without anonymous type

Adapting from this C# MVC tutorial I'm using the following statement to return a specific user to the view
User user = db.Users.Find(id);
if (user == null){return HttpNotFound();}
return View(user);
The Users model has a foreign key to Auctions so the view can iterate over a list of auctions associated with this user.
Auctions has a field called buyerID which corresponds with User.ID so I want to display the buyers name by linking back to User.userName by way of the Auctions.buyerID.
I can do this in SQL statements and LINQ as well but I think there is a simpler way.
How can I use, User user = db.Users.Find(id); and join it to User from the foreign key of Auctions.buyerID?
This is kind of what I thought would work but I can't create a new parameter 'buyerName' when User doesn't have this.
IEnumerable<User> thisUser = from usr in db.Users
join auct in db.Auctions on usr.ID equals auct.UserID
join ur in db.Users on auct.buyerID equals ur.ID
where usr.ID == id
select new User
{
buyerName = ur.userName
};
From other posts like this one I read about setting up a new class but that doesn't seem to stick to the DRY & KISS principles since now I need to have two classes that vary by one parameter located in different locations.
Is there a simpler way to join the table and get the strong typing without creating a new class?
So the result should display the buyers name instead of the buyers ID#
Since I wanted to avoid using anonymous type and I didn't want to create a new class I found a work around by passing a dictionary of user names to the view.
Controller
User user = db.Users.Find(id);
var dict = db.Users.Select(t => new { t.ID, t.userName })
.ToDictionary(t => t.ID, t => t.userName);
ViewData["userDict"] = dict;
Now I just looked up the username based on the buyer ID
View
#{Dictionary<int, String> userList = ViewData["userDict"] as Dictionary<int, String>;}
#{
if (item.buyerID.HasValue)
{
#Html.DisplayFor(modelItem => userList[item.buyerID.Value])
}
}

C# class code with data from multiple tables

Hello and thanks taking your time to help me.
When a user login hes User id is stored in a session, and when he enters my Survey page. I want to the page to display the Surveys that is avalible to him.
My Database Diagram:
I tried to Write the code so First it checks if there is any relations between the user and the surveys in the RelationShip table. I have made sure that part works with debugging because it returns 1 since there is 1 user and one survey + the relation with the correct information.
But it cant write the last part like this: lstItem = db.Survey.Where(x => x.ID == relation).ToList(); so it returns the Correct Survey to my repeater. Since there might be more Surveys avaliple to a user.
My class Code:
public class Surveys
{
public static List<Survey> getAll(int id)
{
List<Survey> lstItem = new List<Survey>();
using (KONE_Entities db = new KONE_Entities())
{
List<User_Survey_Relation> relation = new List<User_Survey_Relation>();
relation = db.User_Survey_Relation.Where(i => i.UserID == id).ToList();
if (relation != null)
{
lstItem = db.Survey.Where(x => x.ID == relation).ToList();
}
}
return lstItem;
}
}
My C# code that binds it to the repeater:
private void BindSurvey()
{
int id = Convert.ToInt32(Session["UserID"].ToString());
rpSurveys.DataSource = Surveys.getAll(id);
rpSurveys.DataBind();
}
So to Clarify what Im asking for/ need help with: My Code will not return the Survey or Surveys that has a relation with the user that is logged on to the site.
Thanks for your time and I hope you can help me.
Compare individual relation values and then add the result to lstItem .
foreach(User_Survey_Relation relatSingle in relation){
lstItem.addRange(db.Survey.Where(x => x.ID == relatSingle.SurveyID).ToList());
}
}
Sorry...dude ...compared the whole class,i have edited it...please check if surveyId has to be compared or Id
You can see from the comments what is wrong with your logic. In terms of how best to solve it, I believe your best option is to join your tables in a single query rather than running 2 queries. The SQL syntax would be:
select s.*
from Survey s
inner join User_Survey_Relation usr on s.ID = usr.SurveyID
where usr.UserID = id
Translating this to LINQ becomes (this is rough - I don't have VS to test):
lstItem = (from db.Survey in survey
join db.User_Survey_Relation in relation on survey.ID
equals relation.SurveyID
where relation.UserID = id
select survey).ToList();
Like I say, you may need to play around with this to iron out any wrinkles. Hopefully you get the idea though.

Multiple cross reference table joins in linq

This has been bugging me for a while since I'm trying to come up with an optimized way of querying this.
So lets say I have 3 cross reference tables that share a common column, where that common column will do a final join on a main table that contains more info.
For example:
Let's say I have the following:
Customers //properties: ID, Name, Address
IEnumberable<CustomerSports> //properties: CustomerID, SportsID
IEnumberable<CustomerLocation> //properties: CustomerID, LocationID
IEnumberable<CustomerPets> //properties: CustomerID, PetsID
So I can make queries such as:
Give me a list of customers that plays lacrosse, football, soccer (CustomerSports)... and have dogs and cats (CustomerPets), that live in New York (CustomerLocation). The lookup tables can be nullable, so Customers could play sports, but have no pets.
Then when I get a list of customers, I'll join that common column (CustomerID) on the customer table to retrieve the ID, Name, and Address.
I was thinking about having the customer table join on each lookup, and then doing a union to fetch the list of customers, but I don't know if that is the correct way of doing it.
As long as you have setup your design correctly then each Customer should have a Sports collection, a Pets collection and a Locations (unless this last one is a one-to-one join?).
If those relationships are setup, then you can query as follows:
var sports = new string[] { "lacrosse", "football", "soccer" };
var pets = new string[] { "cat", "dog" };
var locations = new string[] { "new york" };
var sportyPetLoversInNewYors = db.Customers
.Where(cust => sports.All(sport => cust.Sports.Any(custSport => custSport.Name == sport)))
.Where(cust => pets.All(pet => cust.Pets.Any(custPet => custPet.Name == pet)))
.Where(cust => locations.All(loc => cust.Locations.Any(custLoc => custLoc.Name = loc)))
// could customise the select here to include sub-lists or whatever
.Select();
This assumes that you only want people that have all 6 criteria. If you want people that like at least one of those sports, with at least one of those pets, and (assuming you used more than one location) are in at least one of those locations, the Where expression would change like the following
.Where(cust => cust.Sports.Any(custSport => sports.Contains(custSport.Name)))
Let me know if you need further explanation.
One method of doing this, if i understood what you were after. Allows multiple sports, and multiple pets, or none.
var contacts = from cust in customer
join sport in sports on cust.CustomerID equals sport.CustomerID into multisport from sport in multisport.DefaultIfEmpty()
join loc in location on cust.CustomerID equals loc.CustomerID
join pet in pets on cust.CustomerID equals pet.CustomerID into multipet from pet in multipet.DefaultIfEmpty()
select new
{
cust.CustomerID,
multisport,
loc.LocationID,
multipet
};

Counting in a Linq Query

I have a fairly complicated join query that I use with my database. Upon running it I end up with results that contain an baseID and a bunch of other fields. I then want to take this baseID and determine how many times it occurs in a table like this:
TableToBeCounted (One to Many)
{
baseID,
childID
}
How do I perform a linq query that still uses the query I already have and then JOINs the count() with the baseID?
Something like this in untested linq code:
from k in db.Kingdom
join p in db.Phylum on k.KingdomID equals p.KingdomID
where p.PhylumID == "Something"
join c in db.Class on p.PhylumID equals c.PhylumID
select new {c.ClassID, c.Name};
I then want to take that code and count how many orders are nested within each class. I then want to append a column using linq so that my final select looks like this:
select new {c.ClassID, c.Name, o.Count()}//Or something like that.
The entire example is based upon the Biological Classification system.
Assume for the example that I have multiple tables:
Kingdom
|--Phylum
|--Class
|--Order
Each Phylum has a Phylum ID and a Kingdom ID. Meaning that all phylum are a subset of a kingdom. All Orders are subsets of a Class ID. I want to count how many Orders below to each class.
select new {c.ClassID, c.Name, (from o in orders where o.classId == c.ClassId select o).Count()}
Is this possible for you? Best I can do without knowing more of the arch.
If the relationships are as you describe:
var foo = db.Class.Where(c=>c.Phylum.PhylumID == "something")
.Select(x=> new { ClassID = x.ClassID,
ClassName = x.Name,
NumOrders= x.Order.Count})
.ToList();
Side question: why are you joining those entities? Shouldn't they naturally be FK'd, thereby not requiring an explicit join?

Categories