I have an entity called Entry connected to multiple TimeWindows. I want to clear all time windows, then add new ones. At first I tried:
target.TimeWindows.Clear();
but this didn't really delete them, and only tried to remove the relationship, which caused an exception since there is foreign key from TimeWindows to Entry. Then I thought I should do this:
foreach (var tw in target.TimeWindows)
context.DeleteObject(tw);
but this throw an exception as well, since the collection was modified inside the foreach statement. So I thought of this:
while (target.TimeWindows.Count > 0)
context.DeleteObject(target.TimeWindows.Last());
But now I am a bit concerned about using Count property, because it might cause a SQL SELECT COUNT statement to be executed. Does it? If yes, how can I delete all time windows in Entity Framework?
Calling count on navigation property will cause select only if lazy loading is enabled and the property is not loaded yet. So the first call can cause something like:
SELECT * FROM TimeWindows WHERE TargetId = #targetId
and all count evaluations will just execute on loaded data.
You can also use this to avoid the second exception:
foreach (var tw in target.TimeWindows.ToList())
context.DeleteObject(tw);
Or you can change your database and model to support identifying relation (FK to parent will became part of TimeWindow's PK) and in such case your first code snippet will work.
Related
I get the error "Cannot insert explicit value for identity column in table 'UserPermission' when IDENTITY_INSERT is set to OFF" trying to insert a record as follows:
dbContext.User.Add(someUser);
dbContext.SaveChanges();
That being said, the User file has the custom class UserPermission as one of its parameters, and someUser's UserPermission is not null and has a set ID parameter. Why does this happen and is it possible to avoid getting this error without having to explicitly add a UserPermissionID foreign key parameter in my User model and setting the UserPermission parameter to null?
Thanks in advance.
This issue typically happens when deserializing entities that have related entities in the object graph then attempting to add them. UserPermission is likely an existing record that in the DB is set up with an identity PK, but EF doesn't appear to recognize that in the entity definition. (I.e. set to DatabaseGenerated(DatabaseGeneratedOption.Identity). If it had been you would most likely be seeing a different problem where a completely new duplicate UserPermission was being created.
If someUser, and it's associated someUser.UserPermission are deserialized entities then you need to do a bit of work to ensure EF is aware that UserPermission is an existing row:
void AddUser(User someUser)
{
var existingPermission = _context.UserPermissions.Local
.SingleOrDefault(x => x.UserPermissionId == someUser.UserPermission.UserPermissionId);
if (existingPermission != null)
someUser.UserPermission = existingPermission;
else
_context.Attach(someUser.UserPermission);
_context.Users.Add(someUser);
_context.SaveChanges();
}
In a nutshell, when working with detached entities that a DbContext may not be tracking, we need to check the Local state for any existing tracked instance for that ID. If we find one, we substitute the detached reference for the tracked one. If we don't find one, we attach the detached one before Adding our user.
This still isn't entirely safe because it assumes that the referenced UserPermission will exist in the database. If for any reason a non-existent UserPermission is sent in (row deleted, or fake data) you will get an exception on Save.
Passing detached entity references around can seem like a simple option at first, but you need to do this for every reference within a detached entity. If you simply call Attach without first checking, it will likely work until you come across a scenario where at runtime it doesn't work because the context happens to already be tracking an instance.
I am trying to delete a record if it exists using Entity Framework - I want to generate something like
delete from tbl where id = 1
I don't care whether any records exist, I just want to delete them if they do. I should be able to do with without selecting them first like this:
var record = new tbl { id = 1, rel = new tbl_related { tbl_id = 1 } };
context.tbl.Attach(tbl);
context.tbl.Remove(tbl);
context.SaveChanges();
The syntax might not exactly be there because this isn't a copy and paste, and I've added the foreign key relationship to show that I'm taking this into account.
On running this code the correct SQL is generated by EF and run but I get a System.Data.Entity.Infrastructure.DbUpdateConcurrencyException saying "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded".
However, I'm fully expecting zero rows to be deleted a lot of the time. Any ideas on how I can get around this?
I've been pointed to How to ignore a DbUpdateConcurrencyException when deleting an entity as a possible duplicate. This suggests handling the DbUpdateConcurrencyException which I am now doing (and ignoring), but that post talks about trying to delete rows that might have been deleted - I want to delete rows that might never have existed. I know this might just be a question of semantics because the resolution is the same but I want to highlight that I am doing something that I see as perfectly reasonable and the Framework is not handling it very well.
Thanks
There is an extension for EF on github that allows you to update and delete entities with a single call to the database.
With this extension you can do something like this:
context.tbl.Delete(t => t.Id == 1);
A single call should eliminate your DbUpdateConcurrencyException.
My application allows the user to create a hierarchy of new entities via a UI - let's say it's a "Customer" plus one or more child "Order" entities. The user also assigns each Order entity to an existing "OrderDiscount" entity (think of these as "reference"/"lookup" items retrieved from the database). Some time later, the user will choose to save the whole hierarchy to the database, accomplished like this:-
using (var context = new MyContext())
{
context.Customers.Add(customer);
foreach (var entity in context.OrderDiscounts.Local)
{
objectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
}
context.SaveChanges();
}
The foreach loop changes the state of the OrderDiscount entities to Unchanged, and prevents EF from attempting to insert them into the database, resulting in duplicates.
Great so far, but I've now hit another issue. For reasons I won't go into, the OrderDiscount entities can come from different BLL calls, resulting in a situation where two Orders in the graph may appear to reference the same OrderDiscount (i.e. both have the same PK ID, and other properties), but the entities are different object references.
When I save, the above foreach loop fails with the message "AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges". I can see the two OrderDiscount objects in the context.OrderDiscounts.Local collection, both with the same PK ID.
I'm not sure how I can avoid this situation. Any suggestions?
This article (http://msdn.microsoft.com/en-us/magazine/dn166926.aspx) describes the scenario and provides one possible solution, which is to set just the FK ID (order.OrderDiscountId), and leave the order.OrderDiscount relationship null. Unfortunately it's not feasible in my case, as further down the line I rely on being able to traverse such relationships, e.g. ApplyDiscount(order.OrderDiscount);.
I have a legacy database with a particular table -- I will call it ItemTable -- that can have billions of rows of data. To overcome database restrictions, we have decided to split the table into "silos" whenever the number of rows reaches 100,000,000. So, ItemTable will exist, then a procedure will run in the middle of the night to check the number of rows. If numberOfRows is > 100,000,000 then silo1_ItemTable will be created. Any Items added to the database from now on will be added to silo1_ItemTable (until it grows to big, then silo2_ItemTable will exist...)
ItemTable and silo1_ItemTable can be mapped to the same Item entity because the table structures are identical, but I am not sure how to set this mapping up at runtime, or how to specify the table name for my queries. All inserts should be added to the latest siloX_ItemTable, and all Reads should be from a specified siloX_ItemTable.
I have a separate siloTracker table that will give me the table name to insert/read the data from, but I am not sure how I can use this with entity framework...
Thoughts?
You could try to use the Entity Inheritance to get this. So you have a base class which has all the fields mapped to ItemTable and then you have descendant classes that inherit from ItemTable entity and is mapped to the silo tables in the db. Every time you create a new silo you create a new entity mapped to that silo table.
[Table("ItemTable")]
public class Item
{
//All the fields in the table goes here
}
[Table("silo1_ItemTable")]
public class Silo1Item : Item
{
}
[Table("silo2_ItemTable")]
public class Silo2Item : Item
{
}
You can find more information on this here
Other option is to create a view that creates a union of all those table and map your entity to that view.
As mentioned in my comment, to solve this problem I am using the SQLQuery method that is exposed by DBSet. Since all my item tables have the exact same schema, I can use the SQLQuery to define my own query and I can pass in the name of the table to the query. Tested on my system and it is working well.
See this link for an explanation of running raw queries with entity framework:
EF raw query documentation
If anyone has a better way to solve my question, please leave a comment.
[UPDATE]
I agree that stored procedures are also a great option, but for some reason my management is very resistant to make any changes to our database. It is easier for me (and our customers) to put the sql in code and acknowledge the fact that there is raw sql. At least I can hide it from the other layers rather easily.
[/UPDATE]
Possible solution for this problem may be using context initialization with DbCompiledModel param:
var builder = new DbModelBuilder(DbModelBuilderVersion.V6_0);
builder.Configurations.Add(new EntityTypeConfiguration<EntityName>());
builder.Entity<EntityName>().ToTable("TableNameDefinedInRuntime");
var dynamicContext = new MyDbContext(builder.Build(context.Database.Connection).Compile());
For some reason in EF6 it fails on second table request, but mapping inside context looks correct on the moment of execution.
None of the many questions on this topic seem to match my situation. I have a large data model. In certain cases, only a few of the fields need be displayed on the UI, so for those I replaced the LINQ to Entity query that pulls in everything with an Entity SQL query retrieving only the columns needed, using a Type constructor so that I got an entity returned and not a DbDataRecord, like this:
SELECT VALUE MyModelNameSpace.INCIDENT(incident.FieldA, incident.FieldB, ...) FROM ... AS ...
This works and displays the fields in the UI. And if I make a change, the change makes it back to the entity model when I tab out of the UI element. But when I do a SaveChanges, the changes do not get persisted to the database. No errors show up in the Log. Now if I very carefully replace the above query with an Entity Sql query that retrieves the entire entity, like this:
SELECT VALUE incident FROM MyDB.INCIDENTs AS incident...
Changes do get persisted in the database! So as a test, I created another query like the first that named every column in the entity, which should be the exact equivalent of the second Entity SQL query. Yet it did not persist changes to the database either.
I've tried setting the MergeOption on the returned result to PreserveChanges, to start tracking, like this:
incidents.MergeOption = MergeOption.PreserveChanges;
But that has no effect. But really, if retrieving the entire entity with Entity Sql persists changes, what logical purpose would there be for behaving differently when a subset of the fields are retrieved? I'm wondering if this is a bug?
Gert was correct, the problem was that the entity was not attached. Dank U wel, Gert! Ik was ervan verbluft!
I just wanted to add a little detail to show the full solution. Basically, the ObjectContext has an Attach method, so you'd think that would be it. However, when your Entity SQL select statement names columns, and you create the object using a Type as I did, the EntityKey is not created, and ObjectContext.Attach fails. After trying and failing to insert the EntityKey I created myself, I stumbled across ObjectSet.Attach, added in Entity Framework 4. Instead of failing, it creates the EntityKey if it is missing. Nice touch.
The code was (this can probably be done in fewer steps, but I know this works):
var QueryString = "SELECT VALUE RunTimeUIDesigner.INCIDENT (incident.INCIDENT_NBR,incident.LOCATION,etc"
ObjectQuery<INCIDENT> incidents = orbcadDB.CreateQuery<INCIDENT>(QueryString);
incidents.MergeOption = MergeOption.PreserveChanges;
List<INCIDENT> retrievedIncidents = incidents.ToList<INCIDENT>();
orbcadDB.INCIDENTs.Attach(retrievedIncidents[0]);
iNCIDENTsViewSource.Source = retrievedIncidents;