I have the following model.
Subscription Packages PackageWidgets Widgets
------------ -------- -------------- -------
ID ID < PackageID ID
PackageID > WidgetID >
I am using Entity Framework 4 and a Subscription has a relationship to Package. And Package has a relationship to a list of Widgets.
Using Linq, I am trying to get a listing of all Widgets and if they are included in the current subscription. Perhaps it's due to my SQL background that I'm just not seeing the query in Linq. SQL would involve a SELECT from Widgets, with a LEFT JOIN through a subselect of Subscriptions, Packages and PackageWidgets based on the passed in SubscriptionID.
The output I would expect would be something like WidgetID,IsIncluded such that I would have all Widget IDs and a boolean indicating the inclusion status.
I cannot seem to even get something remotely close to working in order to show what I've done so far.
Can anyone provide me with some insights on how to accomplish my query?
Update:
Here is what I've come close with, but it still doesn't work. Maybe it will help illustrate what I am trying to accomplish though:
from subscription in Subscriptions
where subscription.ID == 3
let subWidgets = subscription.Package.Widgets
from widget in Widgets
join subWidget in subWidgets on widget.ID equals subWidget.ID into joined
from list in joined.DefaultIfEmpty()
select new {
ID = widget.ID
,Selected = subWidget.ID != null
}
Update #2
Thanks to the accepted answer, this is what I ended up going with - which does what I need:
from widget in Widgets
from subWidgets in
from subscription in Subscriptions
where subscription.ID == 3
select subscription.Package.Widgets
orderby widget.ID
select new {
Name = widget.WidgetName,
Available = subWidgets.Contains(widget)
}
Thanks for the assist!
One way to approach it is breaking it up, so something like:
var widgetsInSubscription =
from subscription in Subscriptions
where subscription.ID == 3
from widget in subscription.Package.Widgets
select widget;
var allWidgets =
from widget in Widgets
select new
{
widget.ID,
Selected = widgetsInSubscription.Contains(widget),
};
Or doing it based on the ID's instead of the objects, something like:
var widgetIDsInSubscription =
from subscription in Subscriptions
where subscription.ID == 3
from widget in subscription.Package.Widgets
select widget.ID;
var allWidgets =
from widget in Widgets
select new
{
widget.ID,
Selected = widgetIDsInSubscription .Contains(widget.ID),
};
Keep in mind that you can always do the query yourself if you want - in EF4 you can just call ExecuteStoreQuery with your own SQL
Something like:
from s in db.Subscriptions
from p in db.Packages
from pw in db.PackageWidgets
from w in db.Widgets
where w.ID == pw.WidgetID &&
pw.PackageID == p.ID &&
s.PackageID == p.ID
select w;
Dunno if it works though. However, if you had properties like myPackage.Subscriptions etc, this could probably be simplified a lot.
Related
Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for
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.
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.
KEY NAME REPLACED_BY
1 Foo 1.0 3
2 Bar 1.0 NULL
3 Foo 2.0 NULL
This is supposed to represent a database table with a PRODUCT table. The product entity has 3 properties, KEY, NAME and REPLACED_BY.
What I would like to know is that if I fetch a product from the context:
var product = context.PRODUCT.FirstOrDefault(p => p.KEY == 3);
Is there any way to use a Lambda expression to fetch all the previous versions of the products (using the REPLACED_BY field)? Or do I need to make a foreach-loop?
If you have proper relationships in your database then you could access the previous one like so:
var lastProduct = product.Replaced;//or what ever you call your relationship
with that in mind you could create a function like this:
List<Product> GetPreviousProducts(Product current)
{
List<Product> results = new List<Product>();
Product previous = current.Replaced;
while(previous != null)
{
results.Add(previous);
previous = previous.Replaced;
}
return results;
}
If I understood what you want to do, you may trying joining the table with itself like this: (I don't have anything to debug here so maybe take this with a pinch of salt)
var products = (from p in context.PRODUCT
join r in context.PRODUCT on p.KEY == r.REPLACED_BY
select r)
.Union
(from x in context.PRODUCT where x.KEY == 3 select x);
problem is that you have no way of getting only the products identified by the key unless you have another common identifier (like the name) and maybe keep the version number in a separate column.
I'm trying to filter down the results returned by EF into only those relevant - in the example below to those in a year (formattedYear) and an ordertype (filtOrder)
I have a simple set of objects
PEOPLE 1-M ORDERS 1-M ORDERLINES
with these relationships already defined in the Model.edmx
in SQL I would do something like...
select * from PEOPLE inner join ORDERS on ORDERS.PEOPLE_RECNO=PEOPLE.RECORD_NUMBER
inner join ORDERLINE on ORDERLINE.ORDER_RECNO=ORDERS.RECORD_NUMBER
where ORDERLINE.SERVICE_YEAR=#formattedYear
and ORDERS.ORDER_KEY=#filtOrder
I've tried a couple of approaches...
var y = _entities.PEOPLE.Include("ORDERS").Where("it.ORDERS.ORDER_KEY=" + filtOrder.ToString()).Include("ORDERLINEs").Where("it.ORDERS.ORDERLINEs.SERVICE_YEAR='" + formattedYear + "'");
var x = (from hp in _entities.PEOPLE
join ho in _entities.ORDERS on hp.RECORD_NUMBER equals ho.PEOPLE_RECNO
join ol in _entities.ORDERLINEs on ho.RECORD_NUMBER equals ol.ORDERS_RECNO
where (formattedYear == ol.SERVICE_YEAR) && (ho.ORDER_KEY==filtOrder)
select hp
);
y fails with ORDER_KEY is not a member of transient.collection...
and x returns the right PEOPLE but they have all of their orders attached - not just those I am after.
I guess I'm missing something simple ?
Imagine you have a person with 100 orders. Now you filter those orders down to 10. Finally you select the person who has those orders. Guess what? The person still has 100 orders!
What you're asking for is not the entity, because you don't want the whole entity. What you seem to want is a subset of the data from the entity. So project that:
var x = from hp in _entities.PEOPLE
let ho = hp.ORDERS.Where(o => o.ORDER_KEY == filtOrder
&& o.ORDERLINES.Any(ol => ol.SERVICE_YEAR == formattedYear))
where ho.Any()
select new
{
Id = hp.ID,
Name = hp.Name, // etc.
Orders = from o in ho
select new { // whatever
};
I am not exactly sure what your question is but the following might be helpful.
In entity framework if you want to load an object graph and filter the children then you might first do a query for the child objects and enumerate it (i.e. call ToList()) so the childern will be fetched in memory.
And then when you fetch the parent objects (and do not use .include) enitity framework will able to construct the graph on its own (but note that you might have to disable lazy loading first or it will take long to load).
here is an example (assuming your context is "db"):
db.ContextOptions.LazyLoadingEnabled = false;
var childQuery = (from o in db.orders.Take(10) select o).ToList();
var q = (from p in db.people select p).ToList();
Now you will find that every people object has ten order objects
EDIT: I was in a hurry when I wrote the sample code, and as such I have not tested it yet, and I probably went wrong by claiming that .Take(10) will bring back ten orders for every people object, instead I believe that .Take(10) will bring back only ten overall orders when lazy loading is disabled, (and for the case when lazy loading is enabled I have to actually test what the result will be) and in order to bring back ten orders for every people object you might have to do more extensive filtering.
But the idea is simple, you first fetch all children objects and entity framework constructs the graph on its own.