DeleteObject from Entity Framework loads the whole table? - c#

I have run into a problem with Entity Framework. My code tries to delete 1 or more objects mostly less then 10 from a table.
foreach (var val in vals)
{
int id = Convert.ToInt32(val);
var item = _container.Users.First(x => x.Id == id);
_container.Subscribers.DeleteObject(item);
}
_container.SaveChanges();
The current table "Users" has around 20 000 rows. When i run the code, if it only tries to delete one entity, it take around 10 secounds. I debuged the code and looked in the SQL Profiler. Everything runs smoothly until we hit the DeleteObject() method. It sends this sql query to the database:
exec sp_executesql N'SELECT
-- Yada
FROM [dbo].[Users] AS [Extent1]
WHERE [Extent1].[UserListId] = #EntityKeyValue1',N'#EntityKeyValue1 int',#EntityKeyValue1=1
Why are entity framework loading all the entites in the list? Straaange!
EDIT:
When i changed the code to:
int id = Convert.ToInt32(val);
Users u = new Users();
u.Id = Convert.ToInt32(val);
_container.Users.Attach(s);
_container.Users.DeleteObject(s);
It works like a charm! Still. The code before "_container.Users.First(x => x.Id == id)" did go to the database to find this object, but the after that loaded the whole table.

Below statement anyways makes a call to your database, it is not a strange but feature of EF.
If you have proper primary key created on User table in your database, ideally it will not take more time. It seems you are missing PK.
var item = _container.Users.First(x => x.Id == id);

Related

Add ROW_NUMBER to a LINQ query for specific Entity?

I need an EF query to get the row number of a specific entity.
I've looked at this, and this.
Currently I have it working in this way:
private DbContext Context;
public int GetRowNumberQuery<TEntity>(int entityId)
{
var allEntities = this.Context.Set<TEntity>().ToList();
return allEntities
.Select((entity, index) => new { Index = index, Entity = entity })
.Where(x => x.Entity.Id == entityId)
.Select(x => x.Index)
.SingleOrDefault();
}
Obviously, this is very inefficient as it gets a list of all entities before selecting the index. If I remove the .ToList() in the first line, making the whole thing a LINQ query, it fails at the first Select with NotSupportedException saying:
LINQ to Entities does not recognize the method
'System.Linq.IQueryable1[<>f__AnonymousType12[System.Int32,MyEntityType]]
Select[MyEntityType,<>f__AnonymousType12](System.Linq.IQueryable1[MyEntityType],
System.Linq.Expressions.Expression1[System.Func3[MyEntityType,System.Int32,<>f__AnonymousType1`2[System.Int32,MyEntityType]]])'
method, and this method cannot be translated into a store expression.
Can you please tell me how to get the ROW_NUMBER of a specific entity?
Or is it impossible like this pretty old question suggests?
A) Entity Framework doesn't support ROW_NUMBER() (the examples given use EF to generate a query and then "attach" a number to each returned row client side, starting from 1 and going to n if there are n rows)
B) Even in TSQL the query would be complex:
SELECT TOP 1 ROW_NUMBER() OVER (ORDER BY ID) RN FROM SomeTable WHERE ID = 100
would return NULL if there are no rows or 1 if there is a row with ID 100
You would need something like
SELECT B.RN
FROM
(SELECT ID, ROW_NUMBER() OVER (ORDER BY ID) RN
FROM SomeTable) B
WHERE B.ID = 100
C) Clearly you can create a view/stored procedure that uses ROW_NUMBERand call it from EF

How to set the count property of a related entity without having to perform an extra query or include unnecessary data

I have a class "Campaign" that has a navigation property "Students". The campaign class has an attribute "StudentsCount" to store the amount of students. I do not want to include all the students in the query results. How can I query for campaigns while attaching their respective student counts? I ideally do not want to iterate through all my campaigns after the initial query to grab the counts.
IQueryable<TEntity> query = this._objectSet.AsQueryable(); //this is my campaigns object set
query = query.Where(c => c.UserId == id);
query = query.Include("");
return query.ToArray();
Update: --
Please note that my initial query is grabbing more than one campaign
I'm thinking maybe I could do something with a select but I am not exactly sure how to accomplish this
Querying for counts without loading the collection of child items is called an extra lazy query if you need the term to allow you to Google around this.
In this case, you would do something like this:
var campaign = query.Single();
var studentsQuery = context.Entry(campaign).Collection(c => c.Students).Query();
var count = studentsQuery.Count();
This will materialise the count of entities without bringing them all back.
I ended up adding a computed column with sql onto the Campaign Table.
CREATE FUNCTION dbo.getStudentCount(#studentCount int)
RETURNS int
AS
BEGIN
DECLARE #r int
select #r = COUNT(*) from Student where CampaignId = #studentCount
RETURN #r
END
GO
ALTER TABLE Campaign ADD StudentCount AS dbo.getStudentCount(Id)
this automatically sets the column to be a generated attribute in the EDMX.

NHibernate executing a SELECT before every INSERT

I have an entity with only one field (Value) and the following mapping:
Id(x => x.Value).Column("value").Length(150);
When I execute the following code
using (var tx = Database.BeginTransaction())
{
for (int i = 0; i < 10; i++)
{
var e = new Entity { Id = "Value" + i };
Database.Entities.Add(e);
}
tx.Commit();
}
NHibernate executes a SELECT statement before each INSERT call. Something like this:
NHibernate: SELECT * FROM entity entity_ WHERE entity_.value=#p0; #p0 = 'Value0'
NHibernate: INSERT INTO entity ...
NHibernate: SELECT * FROM entity entity_ WHERE entity_.value=#p0; #p0 = 'Value1'
NHibernate: INSERT INTO entity ...
NHibernate: SELECT * FROM entity entity_ WHERE entity_.value=#p0; #p0 = 'Value2'
NHibernate: INSERT INTO entity ...
If I enable the bulk mode (setting adonet.batch_size) it executes all the SELECT statements first and then the INSERT ones in bulk mode.
Is that the intended behavior? If so, what should I do to avoid that?
This behaviour is correct, related to these facts:
The ID beeing string -> has generator type assigned (the fluent-mapping line in a question)
While discouraged, the session.SaveOrUpdate(e) was used
See: 5.1.4.7. Assigned Identifiers, Extract:
Due to its inherent nature, entities that use this generator cannot be
saved via the ISession's SaveOrUpdate() method. Instead you have to
explicitly specify to NHibernate if the object should be saved or
updated by calling either the Save() or Update() method of the
ISession.
In this case, NHibernate is almost desperate. Why? Because there is no way how to assure, that the assigned id ('value1', 'value2'..) is already in DB or not. So, to be sure if the INSERT or UPDATE should be issued, it must ask the DB. That's why the SELECT before that decision.
Use the Save(e) only, behind the Database.Entities.Add(e) and no supporting infrastructural SELECT will be issued.

Error in MySQL syntax when adding object to objectcontext

I seem to have trouble adding objects to tables that have a 'n to n' relationship.
Tables are defined as follows:
Table A
ID (PRIMARY)
...
...
...
Table B
ID (PRIMARY)
...
...
...
Table C
TableA_ID (index)
TableB_ID (index)
So basically Table C links Table A and B, by their IDs. Using the entity framework we now have an object TableA containing an Entity Collection of TableB entities.
However when I add an existing object of type TableB to the TableA.TableBs entity collection property, I receive an exception:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(SELECT\n TableC.TableA, \n ' at line 1
It seems that I'm trying to do a very normal / common thing, however I've not been successful getting this to work.
C# code:
var database = new DatabaseEntities();
var tableAObject = database.SingleOrDefault(e => e.ID == 1);
var tableBObject = database.SingleOrDefault(e => e.ID == 1);
tableA.TableBEntities.Add(tableBObject);
database.SaveChanges();
Apparently I'm doing something wrong, so my question is, how should I add an object to Table C?

Linq2SQL: Select only some columns, but still able to submit changes

I need to update a column in a table which contains a lot of rows. Each row has a some large TEXT columns in it, which i do not need for my update.
I'm using LinqPAD, and this is roughly, what i wanna do:
(from s in Table
where s.FK_ID == null
select new{s.FK_ID, s.Datum, s.PBNummer}).ToList()
.ForEach(s => s.FK_ID = new Guid(...some new guid here...));
SubmitChanges();
This does not compile, as the properties of an anonymous class type are read-only.
If I do
(from s in Table
where s.FK_ID == null
select s).ToList()
then I can update and save, but all columns are loaded, which takes a very long time and causes memory problems.
Is there a way to only load some columns but still have an object that i can update and save using SubmitChanges? Or do i have to switch to SQL statements?
Way to update specific columns of a database record in Linq to SQL is to create a View on the table containing large columns, and only include the “short” columns:
CREATE VIEW [dbo].[V_FooMax] AS
SELECT OID, ID
FROM dbo.FooMax
Since views based on single tables are updatable, an update on the view is performed as an update on the table:
using (var database = new DataContext())
{
var fooView = database.V_FooMaxes
.Where(foo => foo.OID == OID).FirstOrDefault();
fooView.ID = newID;
database.SubmitChanges();
}
Reference: http://devio.wordpress.com/2011/01/15/updating-a-single-column-in-linq-to-sql-using-a-view/
Also you can look at: http://devio.wordpress.com/2011/01/16/updating-a-single-column-in-linq-to-sql-summary/
Firstly, if you don't have a primary key in the database, then you wouldn't be able to update via Linq-To-Sql. If you have a primary key, but just don't know which it is, you can find it in Linqpad by doing something like
var table = (from t in Mapping.GetTables()
where t.TableName == "[Table]" select t).SingleOrDefault();
(from dm in table.RowType.DataMembers
where dm.DbType != null && dm.IsPrimaryKey
select dm.Name)
.Dump("Primary Key");
Once you know the primary key, you can do something like the following, (I'm assuming the primary key is called Id)
var oldList = (from s in Table
where s.FK_ID == null
select new{s.Id , s.FK_ID, s.Datum, s.PBNummer}).ToList() ;
This is similar to your query, except I have added the primary key
foreach(var r in oldList)
{
Table t = new Table();
t.Id = r.Id ;
Table.Attach(t);
t.FK_ID = new Guid(...some new guid here...));
}
SubmitChanges();

Categories