Trace Entity Framework 4.0 : Extra queries for foreign keys - entity-framework

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();

Related

EF Core completely ignores my selected properties in select

As I understand it, the following code should generate a query containing only the RouteId, RouteNo, and ShipId
var tow = (from t in _context.AllTowData
where t.RouteId == id
orderby t.RouteNo descending
select new TowDefaults {
Id = t.RouteId,
TowNo = t.RouteNo,
ShipId = t.ShipId,
LastTow = t.RouteNo
})
.FirstOrDefault();
However, I get:
SELECT v.route_id, v.route_no, v.tow_id, v.analysis_complete, v.checks_complete, v.cpr_id, v.date_created, v.date_last_modified, v.factor, v.fromportname, v.instrument_data_file, v.instrument_id, v.internal_number, v.mastername, v.message, v.miles_per_division, v.month, v.number_of_samples, v.number_of_samples_analysed_fully, v.prop_setting, v.route_status, v.sampled_mileage, v.serial_no_per_calendar_month, v.ship_speed, v.silk_reading_end, v.silk_reading_start, v.toportname, v.tow_mileage, v.validity, v.year
FROM view_all_tow_data AS v
WHERE v.route_id = '#__id_0'
ORDER BY v.route_no DESC
LIMIT 1
That's every column except the explicitly requested ShipId! What am I doing wrong?
This happens using both a SQL Server and a PostGres database
The property ShipIdis not mapped, either by a [NotMapped] annotation or a mapping instruction. As far as EF is concerned, the property doesn't exist. This has two effects:
EF "notices" that there's an unknown part the final Select and it switches to client-side evaluation (because it's a final Select). Which means: it translates the query before the Select into SQL which doesn't contain the ShipId column, executes it, and materializes full AllTowData entities.
It evaluates the Select client-side and returns the requested TowDefaults objects in which ShipId has its default value, or any value you initialize in C# code, but nothing from the database.
You can verify this by checking _context.AllTowData.Local after the query: it will contain all AllTowData entities that pass the filter.
From your question it's impossible to tell what you should do. Maybe you can map the property to a column in the view. If not, you should remove it from the LINQ query. Using it in LINQ anywhere but in a final Select will cause a runtime exception.

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.

EF 4.1.3 Implenting Rowversion for Concurrency is not working

I have got a BaseEntity class which contains a, byte array called stamp, id and state, which every Entity inherits from I am mapping the stamp to
Property(t => t.Stamp).IsRequired().IsRowVersion();
this is set in BaseEntityConfiguration
which is set like this
public BaseEntityConfiguration<T> : EntityTypeConfiguration<T> where T :B aseEntity
The mapping is done like this
var baseMapConfiguration = new BaseEntityConfiguration<EntityA>();
modelBuilder.Configurations.Add(baseMapConfiguration);
var entityAMap = new EntityAMap(baseMapConfiguration);
The database has Stamp ROWVERSION NOT NULL; on the Table EntityA
I have code that handles DbUpdateConcurrencyException but this does not get caught even though the stamps are different
Also there is no where clause on with the Stamp field which I would expected
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[LastModified] AS [LastModified],
[Extent1].[CreatedDate] AS [CreatedDate],
[Extent1].[Stamp] AS [Stamp]
FROM [dbo].[EntityA] AS [Extent1]
As you seen no where statement I have also tried it from the with a normal mapping set up but still get the same result
**I have found the problem EF is getting the latest Rowversion instead of the passed in one, How can I stop this. **
if (!EntityA.Stamp.Equals(orignal.Stamp))
{
ctx.Entry(orignal).OriginalValues["Stamp"] = xmlFile.Stamp;
}
I was missing these line of codes from the DAL, this is stop EF putting the current stamp as the search criteria.

Entity Framework - Eager load two many-to-many relationships

Sorry for this being so long, but at least I think I got all info to be able to understand and maybe help?
I would like to load data from my database using eager loading.
The data is set up in five tables, setting up two Levels of m:n relations. So there are three tables containing data (ordered in a way of hierarchy top to bottom):
CREATE TABLE [dbo].[relations](
[relation_id] [bigint] NOT NULL
)
CREATE TABLE [dbo].[ways](
[way_id] [bigint] NOT NULL
)
CREATE TABLE [dbo].[nodes](
[node_id] [bigint] NOT NULL,
[latitude] [int] NOT NULL,
[longitude] [int] NOT NULL
)
The first two really only consist of their own ID (to hook other data not relevant here into).
In between these three data tables are two m:n tables, with a sorting hint:
CREATE TABLE [dbo].[relations_ways](
[relation_id] [bigint] NOT NULL,
[way_id] [bigint] NOT NULL,
[sequence_id] [smallint] NOT NULL
)
CREATE TABLE [dbo].[ways_nodes](
[way_id] [bigint] NOT NULL,
[node_id] [bigint] NOT NULL,
[sequence_id] [smallint] NOT NULL
)
This is, essentially, a part of the OpenStreetMap data structure. I let Entity Framework build it's objects from this database and it set up the classes exactly as the tables are.
The m:n tables do really exist as class. (I understand in EF you can build your objects m:n relation without having the explicit in-between class - should I try to change the object model in this way?)
What I want to do: My entry point is exactly one item of relation.
I think it would be best to first eager load the middle m:n relation, and then in a loop iterate over that and eager load the lowest one. I try to do that in the following way
IQueryable<relation> query = context.relations;
query = query.Where( ... ); // filters down to exactly one
query = query.Include(r => r.relation_members);
relation rel = query.SingleOrDefault();
That loads the relation and all it's 1:n info in just one trip to the database - ok, good. But I noticed it only loads the 1:n table, not the middle data table "ways".
This does NOT change if I modify the line like so:
query = query.Include(r => r.relation_members.Select(rm => rm.way));
So I cannot get the middle level loaded here, it seems?
What I cannot get working at all is load the node level of data eagerly. I tried the following:
foreach (relation_member rm in rel.relation_members) {
IQueryable<way_node> query = rm.way.way_nodes.AsQueryable();
query = query.Include(wn => wn.node);
query.Load();
}
This does work and eagerly loads the middle level way and all 1:n info of way_node in one statement for each iteration, but not the Information from node (latitude/longitude). If I access one of these values I trigger another trip to the database to load one single node object.
This last trip is deadly, since I want to load 1 relation -> 300 ways which each way -> 2000 nodes. So in the end I am hitting the server 1 + 300 + 300*2000... room for improvment, I think.
But how? I cannot get this last statement written in valid syntax AND eager loading.
Out of interest; is there a way to load the whole object graph in one trip, starting with one relation?
Loading the whole graph in one roundtrip would be:
IQueryable<relation> query = context.relations;
query = query.Where( ... ); // filters down to exactly one
query = query.Include(r => r.relation_members
.Select(rm => rm.way.way_nodes
.Select(wn => wn.node)));
relation rel = query.SingleOrDefault();
However, since you say that the Include up to ...Select(rm => rm.way) didn't work it is unlikely that this will work. (And if it would work the performance possibly isn't funny due to the complexity of the generated SQL and the amount of data and entities this query will return.)
The first thing you should investigate further is why .Include(r => r.relation_members.Select(rm => rm.way)) doesn't work because it seems correct. Is your model and mapping to the database correct?
The loop to get the nodes via explicit loading should look like this:
foreach (relation_member rm in rel.relation_members) {
context.Entry(rm).Reference(r => r.way).Query()
.Include(w => w.way_nodes.Select(wn => wn.node))
.Load();
}
Include() for some reason sometimes gets ignored when there is sorting/grouping/joining involved.
In most cases you can rewrite an Include() as a Select() into an anonymous intermediary object:
Before:
context.Invoices
.Include(invoice => invoice .Positions)
.ToList();
After:
context.Invoices
.Select(invoice => new {invoice, invoice.Positions})
.AsEnumerable()
.Select(x => x.invoice)
.ToList();
This way the query never should loose Include() information.
//get an associate book to an author
var datatable = _dataContext.Authors
.Where(x => authorids.Contains(x.AuthorId))
.SelectMany(x => x.Books)
.Distinct();

Entity Framework 4.1 Code First - auto increment field on insert for non primary key

My model contains an Order (parent object) and Shipments (child object). The database table for these already have a surrogate key as an auto-increment primary key.
I have the business rule is that for each shipment in the order, we need to have an auto generated "counter" field -- e.g. Shipment 1, Shipment 2, Shipment 3, etc. Shipment model has properties: "ShipmentId", "OrderId", "ShipmentNumber". My attempted implemention is to have ShipmentNumber an int and in code(as opposed to database), query the Shipment collection and do max() + 1.
Here's a code snipet of what I'm doing.
Shipment newShipmentObj = // blah;
int? currentMaxId = myOrderObj.Shipments
.Select(x => (int?) x.ShipmentNumber)
.Max();
if (currentMaxId.HasValue)
newShipmentObj.ShipmentNumber = currentMaxId.Value + 1;
else
newShipmentObj.ShipmentNumber = 1; // 1st one
myOrderObj.Shipments.Add(newShipmentObj);
// etc.. rest of EF4 code
Is there a better way?
I don't really like this as I have the following problems because of potential transaction/concurrency issues.
My Order object also has a autoincrement "counter" -- e.g. Order 1, Order 2, Order 3, ... My Order model has properties: "OrderId", "CustomerId", "OrderNumber".
My design is that I have an OrderRepository but not a ShipmentRepository. The ShipmentRepository could query off the Order.Shipment collection... but with Orders, I have to query directly off the dbcontext, e.g.
int? currentMaxId = (_myDbContext)).Orders
.Where(x => x.CustomerId == 123456)
.Select(x => (int?)x.OrderNumber)
.Max();
However, the above part doesn't work well if I attempt to add multiple objects to the DbContext without committing/saving changes to the database. (i.e. the .Where() returns null... and only works if I use DbContext ".Local", which is not what I want.)
Help! Not sure what the best solution would be. Thanks!
you seem to already have shipmentid that is incremental. you can use it for you shipment number and maybe combined with current date as described here: How to implement gapless, user-friendly IDs in NHibernate? what you are trying to do with Max() is evil. Stay away from it as it can cause problems with getting the same shipment numbers for multiple shipments when the load is high