Get data from SQL table by linq-to-sql using data from collection - entity-framework

I'm using entity framework to connect to database from my application. I have table in SQL, named Orders. It contains such fields as: TransactionId, ParticipantId and is linked to Transactions table which has one to many connection to Participants table. I need to get data from it using List of classes with such properties: TransactionId, ParticipantId, OrganizationId. Linq must meet such conditions: (orders.TransactionId == TransactionId && orders.ParticipantId == ParticipantId && orders.Transaction.Participants.Any(x=> x.Id == OrganizationId)). This should be done by one query, not by multiple, so, please don't recommend foreach or smth like that.

Like #NetMage said, generally we need examples. Assuming that you've got a dbcontext set up, the ask is pretty simple:
public static void GetData(int transactionId, int participantId, int organizationId)
{
using (var db = new MyDbContext())
{
var query =
(
from t in db.Transactions
from o in db.Orders
.Where(w => w.TransactionId == t.TransactionId)
from p in db.Participants
.Where(w => w.TransactionId == t.TransactionId)
where t.TransactionId = transactionId &&
o.ParticipantId = participantId
select new { Order = o, Transaction = t, Participant = p}
);
}
}
Again since we don't have a lot of information here it's hard to do more. You should be able to take it from there. I know I didn't use the organizationId filter, but since I don't know the target shape of the data I'm not sure what the best path would be

Related

Entity Framework Timeout

I have been trying to figure out how to optimize the following query for the past few days and just not having much luck. Right now my test db is returning about 300 records with very little nested data, but it's taking 4-5 seconds to run and the SQL being generated by LINQ is awfully long (too long to include here). Any suggestions would be very much appreciated.
To sum up this query, I'm trying to return a somewhat flattened "snapshot" of a client list with current status. A Party contains one or more Clients who have Roles (ASPNET Role Provider), Journal is returning the last 1 journal entry of all the clients in a Party, same goes for Task, and LastLoginDate, hence the OrderBy and FirstOrDefault functions.
Guid userID = 'some user ID'
var parties = Parties.Where(p => p.BrokerID == userID).Select(p => new
{
ID = p.ID,
Title = p.Title,
Goal = p.Goal,
Groups = p.Groups,
IsBuyer = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
IsSeller = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "seller")),
Journal = p.Clients.SelectMany(c => c.Journals).OrderByDescending(j => j.OccuredOn).Select(j=> new
{
ID = j.ID,
Title = j.Title,
OccurredOn = j.OccuredOn,
SubCatTitle = j.JournalSubcategory.Title
}).FirstOrDefault(),
LastLoginDate = p.Clients.OrderByDescending(c=>c.LastLoginDate).Select(c=>c.LastLoginDate).FirstOrDefault(),
MarketingPlanCount = p.Clients.SelectMany(c => c.MarketingPlans).Count(),
Task = p.Tasks.Where(t=>t.DueDate != null && t.DueDate > DateTime.Now).OrderBy(t=>t.DueDate).Select(t=> new
{
ID = t.TaskID,
DueDate = t.DueDate,
Title = t.Title
}).FirstOrDefault(),
Clients = p.Clients.Select(c => new
{
ID = c.ID,
FirstName = c.FirstName,
MiddleName = c.MiddleName,
LastName = c.LastName,
Email = c.Email,
LastLogin = c.LastLoginDate
})
}).OrderBy(p => p.Title).ToList()
I think posting the SQL could give us some clues, as small things like the order of OrderBy coming before or after the projection could make a big difference.
But regardless, try extracting the Clients in a seperate query, this will simplify your query probably. And then include other tables like Journal and Tasks before projecting and see how this affects your query:
//am not sure what the exact query would be, and project it using ToList()
var clients = GetClientsForParty();
var parties = Parties.Include("Journal").Include("Tasks")
.Where(p=>p.BrokerID == userID).Select( p => {
....
//then use the in-memory clients
IsBuyer = clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
...
}
)
In all cases, install EF profiler and have a look at how your query is affected. EF can be quiet surprising. Something like putting OrderBy before the projection, the same for all these FirstOrDefault or SingleOrDefault, they can all have a big effect.
And go back to the basics, if you are searching on LoweredRoleName, then make sure it is indexed so that the query is fast (even though that could be useless since EF could end up not making use of the covering index since it is querying so many other columns).
Also, since this is query is to view data (you will not alter data), don't forget to turn off Entity tracking, that will give you some performance boost as well.
And last, don't forget that you could always write your SQL query directly and project to your a ViewModel rather than anonymous type (which I see as a good practice anyhow) so create a class called PartyViewModel that includes the flatten view you are after, and use it with your hand-crafted SQL
//use your optimized SQL query that you write or even call a stored procedure
db.Database.SQLQuery("select * from .... join .... on");
I am writing a blog post about these issues around EF. The post is still not finished, but all in all, just be patient, use some of these tricks and observe their effect (and measure it) and you will reach what you want.

A LINQ extension that I wrote will not work with Entity Framework

I have an extension method defined like so:
public static TSource MaxBy<TSource, TResult>(this IEnumerable<TSource> collection, Func<TSource, TResult> func) where TSource : class
{
var comparer = Comparer<TSource>.Default;
TSource maxItem = null;
foreach (var item in collection)
{
if (comparer.Compare(item, maxItem) > 0)
maxItem = item;
}
return maxItem;
}
which I then use in the following LINQ-to-Entities query:
var balancesQuery = from t in db.Transactions
where t.UserId == userId
group t by t.CurrencyCode into tg
let tMaxDate = tg.MaxBy(i => i.TsCreate)
join c in db.Currencies on tg.Key equals c.CurrencyCode
select new { Currency = c, Balance = tMaxDate.Balance }
So what I'm doing is - get the newest transaction (MaxBy TsCreate) in each currency (group by CurrencyCode) and then select the balance against each of those transactions.
My problem is - this does not work with Entity Framework (LINQ-to-Entities; I get:
LINQ to Entities does not recognize the method 'Transaction MaxBy[Transaction,DateTime](System.Collections.Generic.IEnumerable'[Transaction], System.Func'2[Transaction,System.DateTime])' method, and this method cannot be translated into a store expression.
The same query works with LINQ to SQL.
My questions are:
Is there a way to make it work with Entity Framework?
Or maybe there is a better way of querying for the same information, which would work with Entity Framework?
Thanks in advance for any help!
Isn't the query same as:
var balancesQuery = from t in db.Transactions
where t.UserId == userId
group t by t.CurrencyCode into tg
join c in db.Currencies on tg.Key equals c.CurrencyCode
select new {
Currency = c,
Balance = tg.OrderByDescending(i => i.TsCreate).Take(1).Balance
};
You can't use extensions with LINQ queries that actually pull data from the database as it's impossible for this to be turned into SQL. However, you can do this by returning the results into memory using ToArray() or ToList() to trigger the database query executing and then call your extension functions on resultant real data in memory.

Entity Framework Include with condition

I need to filter a dealer based on id and the uncomplete checkins
Initially, it returned the dealer based only on id:
// TODO: limit checkins to those that are not complete
return this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
.FirstOrDefault();
As you can see the requirement is to limit the checkins.
Here's what I did:
var query = from d in this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
select new
{
Dealer = d,
Groups = from g in d.Groups
select new
{
Items = from i in g.Items
select new
{
Group = i.Group,
Observations = i.Observations,
Recommendations = i.Recommendations
}
},
Checkins = from c in d.Checkins
where c.Complete == true
select new
{
Inspections = from i in c.Inspections
select new
{
InspectionItems = i.InspectionItems
}
}
};
var dealer = query.ToArray().Select(o => o.Dealer).First();
return dealer;
It works.
However, I am not convinced I am doing the right thing.
What is the best way to accomplish what I did? A stored procedure maybe?
I am not sure I even have to use Include clause anymore
Thank you.
If you want to load filtered relations with single query you indeed have to execute such projection but you don't need those calls to Include. Once you are building projections includes are not use - you have returned data under your control.
Stored procedure will help you only if you fall back to plain ADO.NET because stored procedures executed through Entity framework are not able to fill related entities (only flattened structures).
Automatic fixupu mentioned by #Andreas requires multiple database queries and as I know it works only if lazy loading is disabled because proxied object somehow doesn't have information about fixup and it still has its internal flags for each relation as not loaded so when you access them for the first time they still execute additional query.
Maybe you can make use of the relation fixup mechanism in the EF ObjectContexts. When you do multiple queries in the same context for entities, that are related by associations, these are resolved.
Assuming your association between Dealers and Checkins is 1:n with navigation properties on each side, you could do like:
var dealer = yourContext.Dealers
.Where(p => p.DealerId == id)
.FirstOrDefault();
if(dealer != null)
{
yourContext.Checkins
.Where(c => c.Complete && c.DealerId == dealer.DealerId)
.ToList();
I have not tested this by now, but since EF recognises that the Checkins, it inserts into the context by the second query belong to the dealer from the first query, corresponding references are created.
#Andreas H:
Awesome, thank you a lot.
I had to adjust your suggestion like this and it worked:
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Include("Inspections")
.Include("Inspections.InspectionItems")
.Where(c => !c.Complete && c.Dealer.DealerId == dealer.DealerId)
.ToList();
}
return dealer;
I still have to use the Include otherwise it won't return the referenced entities.
Note also that Dealer.Groups are unrelated to the Dealer.Checkins.
So if there's no checkins satisfying the condition, Groups still need to be returned.
It's interesting to note that at first, I put the two include for checkins to the dealer
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Where(c => c.Complete && c.DealerId == id)
.ToList();
}
return dealer;
but it returned all the Checkins including those which are not complete.
I don't understand exactly why the latter doesn't work but the former does, how are the entities are resolved. I somehow can intuit that the former returns all data.
Your accepted solution will generate multiple database queries. As Ladislav Mrnka said a projection is the only way to pull your result with one query. The maintance of your code indeed hard. Maybe you could use an IQueryable-Extension that builds the projection dynamically and keep your code clean:
var query = this.ObjectContext.Dealers.SelectIncluding( new List<Expression<Func<T,object>>>>(){
x => x.Groups,
x => x.Groups.Select(y => y.Items),
x => x.Groups.Select(y => y.Items.Select(z => z.Observations)),
x => x.Groups.Select(y => y.Items.Select(z => z.Recommendations)),
x => x.Checkins.Where(y => y.Complete==true),
x => x.Checkins.Select(y => y.Inspections),
x => x.Checkins.Select(y => y.Inspections.Select(z => z.InspectionItems))
});
var dealer = query.First();
return dealer;
You can find the extension at thiscode/DynamicSelectExtensions on github

Can I get EntityFramework 4.1 to return queries that return results that consider data in both the DbSet.Local and database?

Say I have a table in a database called TestDB with a table called Table1 which contains two columns
ID
Name
In this table ,there is two row,
ID 1 and Name 'Row1'
ID 2 and Name 'Row2'
If this is my code
var currObj = Table1.Where(o => o.Name.Contains("Row1")).FirstOrDefault();
currObj.Name = "Row1a";
var a = Table1.Where(o => o.Name.Contains("Row1a")).FirstOrDefault();
var b = Table1.Local.Where(o => o.Name.Contains("Row1a")).FirstOrDefault();
a is going to return null, whereas b is going to return a value.
if I do this though
var c = Table1.Where(o => o.Name.Contains("Row2")).FirstOrDefault();
var d = Table1.Local.Where(o => o.Name.Contains("Row2")).FirstOrDefault();
then c will return something, but d will not.
For me, this seems unintuitive because there is two different places the data could be. For every query I do, I have to look in the database, and the Local object and merge them together. Like if it's changed in the local object, I have to take that into account. Does Entity Framework have any mechanism for this, which would consider BOTH the data in the database AND the local at the same time?
So if I went
var e = Table1.AMagicSolution.Where(o => o.Name.Contains("Row1a")).FirstOrDefault();
var f = Table1.AMagicSolution.Where(o => o.Name.Contains("Row2")).FirstOrDefault();
then e and f would both return something (f from the database and e from the Local object)
Your expectation is little bit strange. EF context works like a unit of work = it is used to process single logical transaction and because of that you should try to design your application in the way that it knows if the record is in the local storage or not.
If for any reason your application doesn't know if the record is in the local storage you should always separate queries to the local storage and to the database! Reasons are performance and unnecessary roundtrips to the database. Use helper like in following example to query the local storage first and only if record is not present in the local storage query the database:
public static Table1 AMagicSolution(this IQueryable<Table1> query, string name)
{
var item = Table1.Local.Where(t => t.Name.Contains(name)).FirstOrDefault();
if (item != null)
{
item = Table1.Where(t => t.Name.Contains(name)).FirstOrDefault();
}
return item;
}
Not much different from your other question.
Table1.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Unchanged)
.Where(o => o.GetType() == typeof(Table1Type)
&& o => o.Name.Contains("Row1a"))
.FirstOrDefault();
From the docs: "The EntityState is a bit field, so state entries for multiple states can be retrieved in one call by doing a bitwise OR of more than one EntityState values."

ADO.NET Entity Framework multiple statement in where clause or a grouped lambda expression

I am new to the ADO.NET Entity Framework Model. Using DotNet Framework 3.5
I have two tables :
viz. customers and city
The customers table refers to a cityname column in the table city (foreign key relationship)
While creating a win c# form i am giving the user to filter customers based on his search choices (viz. name, city, number, etc.) Here is my structure
using(DataContext dc = new DataContext())
{
IEnumerable cust = dc.customers;
if(name != null) {
cust = cust.Where<customers>(c => c.name == name);
}
if(mobile != null) {
cust = cust.Where<customers>(c => c.mobile == mobile);
}
if(city != null) {
cust = cust.Where<customers>(c => c.city.cityname == city); //ERROR HERE
}
}
I get a NullPointerException, since the EntityReference.Load method is not called upon. Quite logical point and I agree to it.
I would like some advice on how to either call the load method in the current architechure. Is it possible to somehow do this :
c.cityReference.Load();
c.city.cityname == city
Or possibly some lambda expression (I am new to it) which induces both the statements? Ne suggestions?
I am ready to change the current architecture if anyone has a better advice.
Try
IEnumerable cust = dc.customers.Include("city");
Check out this post for reference.