So I am using EF, I have the following entities:
Website
Sector
Product
Attribute
AttributeTag
The relationships are as follow:
I need to retrieve something that is not directly linked to the table. Such as products which needs a Sector object in order to retrieve only the specific Products using something such as sector.Products.
But what if I need to retrieve all Products under a given Website instead of it's parent Sector ?
In my specific situation, my questions are:
1) How can I retrieve all the products given a specific website_id - (Disregarding the sector)
2) How can I retrieve all the products that have a specific tag_id + website_id. (Also retrieve it's corresponding Attribute)
Help is appreciated. Thanks!
Assuming you've got both side navigation properties :
you will have a List<Sector> SectorList in Product Entity.
you will have a List<Product> ProductList in Sector Entity.
(sectors_products won't be present as an entity, as it's not needed in an object world).
you will have a Website Website in Sector Entity
you will have a List<AttributeTag> AttributeTagList in Product Entity;
(products_tags won't be present as an entity, as it's not needed in an object world).
1) something like :
var result = ProductEntities
.Where(p => p.SectorList
.Any(s => s.WebSite.Id == <your_website_id>)
);
2) something like (taking 1) as base query)
result = result
.Where(p => p.AttributeTagList
.Any(at => at.Id == <your_tag_id>)
);
or all in one
var result = ProductEntitites
.Where(p =>
p.SectorList.Any(s => s.WebSite.Id == <your_website_id>) &&
p.AttributeTagList.Any(at => at.Id == <your_tag_id>)
);
The relationships in your schema form a pathway. If you want to figure out the relationships between two entity sets, you have to follow that pathway and query all the entities in between.
var part1 = (from w in Websites
from s in Sectors
from p in s.Products
where s.Website equals w
&& w.website_id equals web_id
select p).Distinct();
var part2 = from p in part1
let attr = p.Attributes.Where(a => a.tag_id + web_id == target_val)
where attr.Any()
select new { p, attr };
If I understand your schema correctly, that should pull down the data to answer both parts of your question.
Related
I have a few tables and this is what I need to achieve.
This gets all the rows from one table
var FRA = from prod in _cctDBContext.Fra
where prod.ActTypeId == 1
From within that, I get all the rows where ActTypeID.
Then I need to query another table from with the ID's get from that
foreach (var item in FRA)
{
var FRSA = _cctDBContext.Frsa
.Select(p => new { p.Fraid, p.Frsa1,
p.Frsaid, p.CoreId,
p.RelToEstId, p.ScopingSrc,
p.Mandatory })
.Where(p => p.Fraid == item.Fraid)
.ToList();
}
I then need to push each one of these to Entity Framework. I usually do it this way:
foreach (var item in FRA)
{
var FinanicalReportingActivity = new FinancialReportingActivity { FinancialReportingActivityId = item.Fraid, ScopingSourceType = item.ScopingSrc, Name = item.Fra1, MandatoryIndicator = item.Mandatory, WorkEffortTypeId = 0 };
_clDBContext.FinancialReportingActivity.AddRange(FinanicalReportingActivity);
}
But because I have used 2 for each loops, I cannot get the variables to work because I cannot find a way to get local variables as the entity context.
Can anyone think of a better way to code this?
Thanks
It looks like you can do this as a single join:
var query =
from prod in _cctDBContext.Fra
where prod.ActTypeId == 1
join p in _cctDBContext.Frsa on prod.Fraid equals p.Fraid
select new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
};
It looks like you are loading data from one set of entities from one database and want to create matching similar entities in another database.
Navigation properties would help considerably here. Frsa appear to be a child collection under a Fra, so this could be (if not already) wired up as a collection within the Fra entity:
Then you only need to conduct a single query and have access to each Fra and it's associated Frsa details. In your case you look to be more interested in the associated FRSA details to populate this ReportingActivity:
var details = _cctDBContext.Fra
.Where(x => x.ActTypeId == 1)
.SelectMany(x => x.Frsa.Select(p => new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
}).ToList();
though if the relationship is bi-directional where a Fra contains Frsas, and a Frsa contains a reference back to the Fra, then this could be simplified to:
var details = _cctDBContext.Frsa
.Where(x => x.Fra.ActTypeId == 1)
.Select(p => new
{
p.Fraid,
p.Frsa1,
p.Frsaid,
p.CoreId,
p.RelToEstId,
p.ScopingSrc,
p.Mandatory
}).ToList();
Either of those should give you the details from the FRSA to populate your reporting entity.
I have a database that contains 3 tables:
Phones
PhoneListings
PhoneConditions
PhoneListings has a FK from the Phones table(PhoneID), and a FK from the Phone Conditions table(conditionID)
I am working on a function that adds a Phone Listing to the user's cart, and returns all of the necessary information for the user. The phone make and model are contained in the PHONES table, and the details about the Condition are contained in the PhoneConditions table.
Currently I am using 3 queries to obtain all the neccesary information. Is there a way to combine all of this into one query?
public ActionResult phoneAdd(int listingID, int qty)
{
ShoppingBasket myBasket = new ShoppingBasket();
string BasketID = myBasket.GetBasketID(this.HttpContext);
var PhoneListingQuery = (from x in myDB.phoneListings
where x.phonelistingID == listingID
select x).Single();
var PhoneCondition = myDB.phoneConditions
.Where(x => x.conditionID == PhoneListingQuery.phonelistingID).Single();
var PhoneDataQuery = (from ph in myDB.Phones
where ph.PhoneID == PhoneListingQuery.phonePageID
select ph).SingleOrDefault();
}
You could project the result into an anonymous class, or a Tuple, or even a custom shaped entity in a single line, however the overall database performance might not be any better:
var phoneObjects = myDB.phoneListings
.Where(pl => pl.phonelistingID == listingID)
.Select(pl => new
{
PhoneListingQuery = pl,
PhoneCondition = myDB.phoneConditions
.Single(pc => pc.conditionID == pl.phonelistingID),
PhoneDataQuery = myDB.Phones
.SingleOrDefault(ph => ph.PhoneID == pl.phonePageID)
})
.Single();
// Access phoneObjects.PhoneListingQuery / PhoneCondition / PhoneDataQuery as needed
There are also slightly more compact overloads of the LINQ Single and SingleOrDefault extensions which take a predicate as a parameter, which will help reduce the code slightly.
Edit
As an alternative to multiple retrievals from the ORM DbContext, or doing explicit manual Joins, if you set up navigation relationships between entities in your model via the navigable join keys (usually the Foreign Keys in the underlying tables), you can specify the depth of fetch with an eager load, using Include:
var phoneListingWithAssociations = myDB.phoneListings
.Include(pl => pl.PhoneConditions)
.Include(pl => pl.Phones)
.Single(pl => pl.phonelistingID == listingID);
Which will return the entity graph in phoneListingWithAssociations
(Assuming foreign keys PhoneListing.phonePageID => Phones.phoneId and
PhoneCondition.conditionID => PhoneListing.phonelistingID)
You should be able to pull it all in one query with join, I think.
But as pointed out you might not achieve alot of speed from this, as you are just picking the first match and then moving on, not really doing any inner comparisons.
If you know there exist atleast one data point in each table then you might aswell pull all at the same time. if not then waiting with the "sub queries" is nice as done by StuartLC.
var Phone = (from a in myDB.phoneListings
join b in myDB.phoneConditions on a.phonelistingID equals b.conditionID
join c in ph in myDB.Phones on a.phonePageID equals c.PhoneID
where
a.phonelistingID == listingID
select new {
Listing = a,
Condition = b,
Data = c
}).FirstOrDefault();
FirstOrDefault because single throws error if there exists more than one element.
I have two tables - "Customer" table and "Blacklist" customer table. When I blacklist a customer, I put the customerid as a foreign key to Blacklist table.
What I want is to get CusId and Name that are not in the BlackList Table.
How can I code this Entity Framework C#?
Customer
---------
(CusId,Name,Telephone,Email)
Blacklist
---------
(CusId)
What you want is something like the following:
db.Customers
.Where(c => !db.Blacklists
.Select(b => b.CusId)
.Contains(c.CusId)
);
EF will happily turn that into a sub-query that will run quite well.
This pattern works for static lists (creates an IN(a, b, c) expression) as well as other tables. You can use it to check for being in the list or not in the list.
If you want to test it and see the SQL it generates, I strongly recommend LINQPad (it is free). That's what I use to test out little ideas in LINQ all the time.
How about something like this:
var subselect = (from b in BlackList select b.CusId).ToList();
var result = from c in Customer where !subselect.Contains(c.CusId) select c;
Any() is the best performance :
db.Customers.Where(c => !db.Blacklists.Any(b => b.CusId == c.Id));
If you also need columns of Blacklists , use join with condition : b.CusId == c.Id
IF in table BlackList yo have a List of Customer You can perform something like
IEnumerable<Customer> model = (from c in db.Customer
from b in c.BlackList.DefaultIfEmpty()
where b.CusID== null
select new Customer
{
CusId= c.OrderID
}).ToList();
This query I used and it works perfectly:
var query = _context.Customer.Where(x => x.Id == id && !x.BlackList.Any(z => x.Id == x.CustId)).ToList();
i've got a List of objects, lets call them Product, which each of them contains a bunch of properties and also a List of Version (which are also objects).
Version also has a bunch of properties and does contain a List of Customer (which again are objects).
Customer again has properties, one of them is its ID (=Guid).
What i try to do is to make a List of Product, selected by a certain ID of its Product.VersionList.Version.ID.
I would prefere a join query, but every efficient way is welcome. I tried so far this, but because i have only a single ID to compare with, i don't know how to construct the join.
lp = List<Entity.Product>;
g = GetGuid();
var query = from product in Entity.ProductCollection
join g in g
on product.Version.Where(x => x.id == g)
select product;
lp.AddRange(query);
I'm guessing you mean:
var query = from product in Entity.ProductCollection
where product.Version.Any(x => x.id == g)
select product;
i.e. select all the products that have a version where the id matches the guid you were thinking of.
Note that joining to the versions would cause product duplication if any product has multiple matching versions.
Try this .... May be you wants more deep digging on it..
var query = from Product product in pc
from varsion in product.Version
let v= varsion as Entity.Version
where v.id == g
select product;
var query = Entity.ProductCollection.Where(p => p.Version.Any(v => v.Id == g));
You can use Any rather than having to do a self join.
I would like to get the list of albums (Distinct) which was sung by the artistId=1
I am very new to LINQ to SQL and do not know how to join multiple tables. Please see the database diagram below:
alt text http://a.imageshack.us/img155/8572/13690801.jpg
SingBy is the middle table between Track and Artist.
How could I achieve this?
var albums = from singer in artist
from sb in singby
from t in track
from a in album
where singer.artistId == 1 &&
sb.artistId == 1 &&
sb.trackId == t.trackId &&
a.albumId == track.albumId
select a;
I'm sure there must be a better way. You should look into creating Navigation Properties on your entities. Navigation Properties are like foreign keys.
Edit - corrected to get albums, not artists.
Now, I wrote the codes like the following and it works.
var albums = (from a in db.artists
where a.artistId == 1
join sb in db.singbies on a equals sb.artist
join t in db.tracks on sb.track equals t
join al in db.albums on t.album equals al
select al).Distinct();
return albums.ToList() as List<album>;
I tested the Chad's version and it works too. I would like to know which way is better and good for query optimization? Thanks all.
If you have all the foreign key relationship defined, you should be able to issue call like below:
dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).ToList();
This is relying on Linq to perform lazy load for Track and Singby automatically when required. Obviously this is not optimal to use when you have a large set of data in the db and performance is critical. You can chain the query with GroupBy or Distinct operation to return only the distinct set such as
dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).Distinct().ToList();
I would like to get the list of albums
(Distinct) which was sung by the
artistId=1
DBDataContext = new DBDataContext();
album[] = db.artists.Where(a => a.artistId == 1) /* Your artist */
.SelectMany(a => a.singbies) /* Check if `singby` converted to `singbies` */
.Select(sb => sb.track) /* The tracks */
.Select(t => t.album) /* The albums */
.GroupBy(al => al.albumId) /* Group by id */ /* "Distinct" for objects */
.Select(alG => alG.First()) /* Select first of each group */
.ToArray();
IEnumerable<Album> query =
from album in myDC.Albums
let artists =
from track in album.Tracks
from singBy in track.SingBys
select singBy.Artist
where artists.Any(artist => artist.ArtistId == 1)
select album;
List<int> Ids = dc.Albums.Where(a => a.Track.Singby.ArtistId == 1).Select(a=> a.albumId).Distinct().ToList();
List<Album> distinctAlbums = dc.Albums.Where(a => distinctAlbumIds.Contains(a.albumId)).ToList();
Hey TTCG, above is the simplest way to do it. This is because doing a Distinct on a List of objects won't do it based on the albumId.
Either you do it in two steps as above, or, you write your own Album Comparer which specifies uniqueness based on AlbumId and pass it to the Distinct call on a List.
NOTE:
The above will only work if you've defined the constraints in your DBML, but better still in your DB.
For best practices, always define your relationships IN THE DATABASE when using Linq to SQL, as Linq to SQL is not like EF, or NHibernate, in that is does not "abstract" your db, it simply reflects it. It's a tool for Data Driven Design, not Domain Driven, so define the relationships in the db.