Where Query In One-One RelationShip in EF Repository Pattern - entity-framework

Following is my database architecture, three entities:
Load
ID_Load
From
To
Bids[]
Bid
ID_Bid
ID_Load
Amount
Load
Order
Order
ID_Order
Status
Amount
Bid
Load
Load and Bid : One to Many -- Load can receive multiple Bids
Order and Bid : One to One -- A single bid will be mapped with a order
Order and Load : One to One -- An order can have only one Bid
As you can see above I don't have ID_Load or ID_Bid properties in the order Entity as EF generate these automatically in the database, so the properties for these two fields are not added in the Order Entity
I am currenlty using Repository pattern with Code First in my project.
Now I want to get the Order information by ID_Load/ID_Bid (as there is a one to one relationship of Bid-Order and Load-Order table)
I don't want to run any SP or Select query in the application, but having issue while doing this.
If I run SQL I will have to write following query:
Select * from Order Where ID_Load = 123
Or
Select * from Order Where ID_Bid = 123
What is the alternative of this in EF/Repository Patterns in such situations.
Can anyone please help me with this.
Thanks,

Get order by Bid ID:
long bidId; // Id of Bid
var order = dbContext.Orders.Single(o => o.Bid.ID_Bid == bidId);
Get order by Load ID:
long loadId; // Id of Load
var order = dbContext.Orders.Single(o => o.Load.ID_Load == loadId);
If Bid and Load are optional for Order, use SingleOrDefault instead of Single. Single will throw an exception if the queried item does not exist, SingleOrDefault will return null.
Alternatively, if you need to load the Bid using its ID anyway:
var bid = dbContext.Bids.Find(bidId);
var order = bid.Order;
This can be optimized by forcing eager loading:
var bid = dbContext.Bids.Include(b => b.Order).SingleOrDefault(b => b.ID_Bid == bidId);
var order = bid.Order;

Related

How to use Where condition inside Include in entity framework LINQ?

My sample code lines are,
var question = context.EXTests
.Include(i => i.EXTestSections.Where(t => t.Status != (int)Status.InActive))
.Include(i => i.EXTestQuestions)
.FirstOrDefault(p => p.Id == testId);
Here Include was not supporting Where Clause. How can I modify above code?
You have a sequence of ExTests. Every ExText has zero or more ExTestSections, Every Extest also has a property ExtestQuestions, which is probably also a sequence. Finally every ExTest is identified by an Id.
You want a query where you get the first ExTest that has Id equal to testId, inclusive all its ExTestQuestions and some ExTestSections. You want only those ExTestSections whith an InActive status.
Use Select instead of Using
One of the slower parts of database queries is the transfer of the data from the DBMS to your process. Hence it is wise to limit it to only the data you actually plan to use.
It seems that you have designed a one-to-many relation between ExTests and its ExTestSections: every ExTest has zero or more ExTestSections and every ExTestSection belongs to exactly one ExTest. In databases this is done by giving the ExTestSection a foreign key to the ExTest that it belongs to. It might be that you've designed a many-to-many relation. The principle remains the same.
If you ask an ExTest with its hundred ExTestSections, you get the Id of the the ExTest and hundred times the value of the foreign key of the ExTestSection, thus sending the same value 101 times. What a waste.
So if you query data from the database, only query for the data you actually plan to use.
Use Include if you plan to update the queried data, otherwise use Select
Back to your question
var result = myDbContext.EXTests
.Where(exTest => exTest.Id == testId)
.Select( exTest => new
{
// only select the properties you plan to use
Id = exTest.Id;
Name = exTest.Name,
Result = exText.Result,
... // other properties
ExTestSections = exTest.Sections
.Where(exTestSection => exTestSection.Status != (int)Status.InActive)
.Select(exTestSection => new
{
// again: select only those properties you actually plan to use
Id = exTestSection.Id,
// foreign key not needed, you know it equals ExTest primary key
// ExTestId = exTestSection.ExtTestId
... // other ExtestSection properties you plan to use
})
.ToList(),
ExTestQuestions = exTest.ExTestQuestions
.Select( ...) // only the properties you'll use
})
.FirstOrDefault();
I've transferred the test on equal TestId to a Where. This would allow you to omit the Id of the requested item: you know it will equal testId, so not meaningful to transfer it.

Entity Framework Conditional Count of Navigation Property 2 levels down

Just starting out with Entity Framework and am trying to work out how you would do something like this....
Say I have the following entities, Customers that have Orders that have OrderLineItems which are linked to Products. I would like to return the name of every customer with a count of the number of times they have ordered a particular product.
I have seen examples of using .Count() but these have always been for the first navigation property i.e. number of orders per customer.
Would appreciate some guidance here.
Something like this should work, where context is your DbContext instance.
It will return an IEnumerable<dynamic>, although obviously you could make a class to hold the results.
// The product to count
var productId = 12345;
context.Customers.Include("Orders.OrderLineItems.Products")
.Select(customer =>
new {
CustomerName = customer.Name,
ProductCount = customer.Orders
.SelectMany(o => o.OrderLineItems)
.SelectMany(i => i.Products.Where(p => p.Id = productId).Count()
});
The Include() extension method is useful, it will make sure that the resulting SQL query joins the relevant tables together - otherwise multiple queries would be executed for each customer (one to get orders, another for line items and a final one for products).

Trace Entity Framework 4.0 : Extra queries for foreign keys

In the following example, we insert an entity called taskinstance to our context. we have a foreign key FK_Contract that we set at 2.
entity.FK_Contract = 2;
context.TaskInstances.AddObject(entity);
The query generated by entity framework is a simple insert. (everything is fine)
However, the following query works differently.
int contractId = context.Contracts.Where((T) => T.Name == contractName).Single().Id;
entity.FK_Contract = contractId;
context.TaskInstances.AddObject(entity);
In the trace created by entity framework we see without surprise the query selecting the Id according a contractName but we also see an extra request looking like:
select id,... from [TaskInstances] WHERE [Extent1].[FK_Task] = #contractId
This extra query leads to many problems, especially when we work with a foreign table with millions of record. The network goes down!
Therefore we 'd like to figure out the purpose of this extra query and the way to make it disappear.
It looks like the extra query is populating a collection of tasks on the returned Contract object. Try projecting just the column you want:
int contractId = context.Contracts
.Where(T => T.Name == contractName)
.Select(T => T.Id)
.Single();

Many-to-Many Inserts with Entity Framework

Say I have two entities with about 20 properties per entity and a Many-to-Many relationship like so:
User (Id int,Name string, .......)
Issue (Id int,Name string, .......)
IssueAssignment (UserId,RoleId)
I want to create a new Issue and assign it to a number of existing Users. If I have code like so:
foreach(var userId in existingUserIds)
{
int id = userId
var user = _db.Users.First(r => r.Id == id);
issue.AssignedUsers.add(user);
}
_db.Users.AddObject(user);
_db.SaveChanges();
I noticed it seems terrribly inefficient when I run it against my SQL Database. If I look at
the SQL Profiler it's doing the following:
SELECT TOP(1) * FROM User WHERE UserId = userId
SELECT * FROM IssueAssignment ON User.Id = userId
INSERT INTO User ....
INSERT INTO IssueAssignment
My questions are:
(a) why do (1) and (2) have to happen at all?
(b) Both (1) and (2) bring back all fields do I need to do a object projection to limit the
fields, seems like unnecessary work too.
Thanks for the help
I have some possible clues for you:
This is how EF behaves. _db.Users is actaully a query and calling First on the query means executing the query in database.
I guess you are using EFv4 with T4 template and lazy loading is turned on. T4 templates create 'clever' objects which are able to fixup their navigation properties so once you add a User to an Issue it internally triggers fixup and tries to add the Issue to the User as well. This in turns triggers lazy loading of all issues related to the user.
So the trick is using dummy objects instead of real user. You know the id and you only want to create realtion between new issue and existing user. Try this (works with EFv4+ and POCOs):
foreach(var userId in existingUserIds)
{
var user = new User { Id = userId };
var _db.Users.Attach(user); // User with this Id mustn't be already loaded
issue.AssignedUsers.Add(user);
}
context.Issues.AddObject(issue);
context.SaveChanges();

Entity Framework, How to include related entities in this example

I have a table AccountSecurity which is a many-to-many table that relates Account entities and Securities. When I write the query below it returns all Securities that satisfy the where clause. However each Security instance in the list no longer has the reference to the AccountSecurity it came from. So when I do list[0].AccountSecurity it is empty. Is there anyway to include that information? I know I can rewrite the query to return AccountSecurities instead and use .Include("Security") on that, but I wonder if it can be done another way.
var list = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
UPDATE
Of course if I do two queries the graph gets populated properly, there has to be a way to do this in one shot.
var securities = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
//this query populates the AccountSecurities references within Security instances returned by query above
var xref = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec).ToList();
var list = (from sec in base.context.Securities
.Include("AccountSecurity")
where sec.AccountSecurities.Any(as => as.AccountId == accountId)
select sec).ToList();
Try this:
var list = (from acctSec in base.context.AccountSecurities.Include("Security")
where acctSec.AccountId == accountId
select acctSec).ToList();
Then simply use the Security property as needed, and since it's read at the same time AccountSecurities is (single SQL with join), it will be very efficient.