(3am here, so bear with me!)
This is EF6. I have many-to-many relationship between entities Procedures and Points. I create a new Procedure object named P and add one of the existing Point objects to its Points collection. I then send P to EF for saving:
context.Procedures.Add(P);
context.SaveChanges();
Unfortunately this tries to INSERT the Point object too into the database, which obviously fails with duplicate primary key error.
Among serveral other things, I checked the value of context.ChangeTracker.Entries() and to my astonishment, it contains 21 entries instead of just 2. Upon further investigation, it looks like EF is creating entries for the recursive Procedure > Point > Procedure relation. How can I fix this problem?
Edit
(4am now, :))
I have made it to work with the following code:
var Temp = P.Points.ToArray();
P.Points.Clear();
foreach (var t in Temp)
P.Points.Add(context.Points.Find(t.Id));
context.Procedures.Add(P);
Is there really the correct way of doing it?
Related
In my database, I have a parent table and a child table with foreign key pointing to the parent.. at some point I have to clear all rows from both tables.
I used the following code in Entity Framework:
using (MuseumDBEntities db = new MuseumDBEntities())
{
db.Database.ExecuteSqlCommand("truncate table childTable");
db.Database.ExecuteSqlCommand("truncate table parentTable");
}
I get an exception at the second truncate because of foreign key, although I am clearing child table first.
What should I do? Is there another way to delete all rows of both tables?
I don't know if a foreach loop over all rows is practical.
That's SQL server's fault!
You have two ways to achieve your goal:
Drop the foreign keys, then truncate the table and then recreate the foreign key (I don't recommend this solution because it is too much work and usually not worth it)
Instead of Truncate use Delete (I usually use this method)
Your code will look like this:
using (MuseumDBEntities db = new MuseumDBEntities())
{
db.Database.ExecuteSqlCommand("Delete from childTable");
db.Database.ExecuteSqlCommand("Delete from parentTable");
}
Since the two delete statements have no conditions (no where clause), all rows will be deleted from the tables.
Interfacing to SQL Server 2008R2:
I have a linq expression:
IQueryable<xxx> xxxResult =
(from t in _context.xxxx.AsNoTracking().Include("yyy")
where t.zzz >= lowNumber
&& t.zzz <= highNumber
&& t.qqq == someValue
select t);
(It probably doesn't matter on the exact query, but it's there in case it does.)
Linq generated SQL that the SQL Server generated a terrible plan, and, since I can't add index/join hints, I created a stored procedure that wrapped the SQL that the above Linq expression generated.
I know I should be able to access the stored procedure through Entity Framework, but I'm using a previous project that used a very light code-first implementation (no .edmx file, for instance) and I'm kinda new to the whole EF thing and didn't know how to tie the new procedure into EF. I know it can be done, but I am trying to call the procedure directly.
I worked this out:
IQueryable<xxx> xxxResult =
_context.xxxx.SqlQuery("GetData #p0, #p1, #p2", someValue, lowNumber, highNumber)
.AsNoTracking().AsQueryable();
This seems to work, except for one problem. When iterating over the linq queryable, everything works swimmingly. But, when I use the stored procedure, I get duplicate records.
For instance, if I have an xxx record that includes 3 yyy records in a collection, I get a single xxx record from the linq expression and it, as expected, includes 3 yyy records in the internal collection.
The stored procedure, for the same dataset, iterating over the queryable returns three xxx records, EACH of which contain the same 3 yyy records.
Again, the stored procedure executes the exact same SQL that the linq expression generated.
Why is that? Any ideas?
(Again, new to EF, so please forgive errors in terminology.)
I believe that EF is seeing your results as duplicate based on the primary key you have defined. In EF5, this would be defined using the "Entity Key" property on the fields which uniquely define the entity (a multi-part primary key would have this set on multiple fields).
If your procedure returns a record that matches one that it already returned (based soley on the primary key fields) then it will return a reference to the previous record.
Your LINQ expression uses .AsNoTracking which should prevent this caching behavior.
I'm guessing that the .AsNoTracking() using the stored proc occurs after it has been cached and doesn't have the effect you are looking for.
Make sure that you have your primary keys set properly on your model.
Here's an article that describes the behavior with a view:
http://jepsonsblog.blogspot.in/2011/11/enitity-framework-duplicate-rows-in.html which should be similar to what you are seeing with the stored procedure.
It looks like in Code First, you would use the [Key] annotation to specify your unique keys: http://msdn.microsoft.com/en-us/data/jj591583.aspx
I'm trying to archiving an entity of a table. There are couple of ways to do this. One of them is to create IsArchived column and set it to true when an entity is deleted or put into history. One of the disadvantage of this design will make specified table so heavy.
Another way to do this is to create the duplication of the class of specified entity to be logged, make another table, and adding it to log table with the help of AutoMapper. In this case i need lots of duplicate classes of entities which needed to be archived.
Is there any other solutions for archiving specified entities?
The best way would be to add a nullable ArchivedTimeStamp column to the table. This way, it is possible to tell if the row was archived or not, and if so, when it was archived.
If you are worried about the table size, you can partition the table and automatically move the archived rows onto a secondary / slower physical disk. You can even partition it in such a way that only rows that was, let say, archived over a year ago, must be moved to the secondary partition.
More info on on SQL archiving using partitioning can be found on http://www.mssqltips.com/sqlservertip/2780/archiving-sql-server-data-using-partitioning/
You could have more than one database, with the same schema. You can then open a couple contexts, one to each database, using a different connection string. Query one, attach the entities to the other, and save.
I've never done this, but it should work. You might run into trouble since the entities are going to be attached to the source context and cannot be attached to the destination, but there are ways to unattach and reattach the entities.
I have implemented a soft delete for the purposes of undo. My answer shows how to overcome some of the problems normally associated with soft deletes - i.e. joins and indexes. It suits my purposes well. However, if it was used for archiving then the tables would grow forever.
Your other idea is to create duplicate classes and use automapper. That sounds like a lot of extra coding.
I think you could create a database with the same schema - except, perhaps, the primary keys would not be database generated, and foreign keys not enforced. Then override the delete so that the data is copied over.
Something like this:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries()
.Where(p => p.State == EntityState.Deleted
&& p.Entity is ModelBase))//I have a base class for entities with a single
//"ID" property - all my entities derive from this
CustomDelete(entry);
return base.SaveChanges();
}
private void CustomDelete(DbEntityEntry entry)
{
var e = entry.Entity as ModelBase;
string tableName = GetTableName(e.GetType());
string sql = String.Format(#"INSERT INTO archive.{0} SELECT * FROM {0} WHERE ID = #id;
DELETE FROM {0} WHERE ID = #id", tableName);
Database.ExecuteSqlCommand(
sql
, new SqlParameter("id", e.ID));
entry.State = EntityState.Detached;
}
Note that in EF6 you could also override the delete by altering the sql in the migration file when mapping to stored procedures is used
I have 2 entities, let's say, Trip and Activity. The relationship between them is many to many so a join table is created automatically by EF.
Entity Trip attributes:
-Id (PK) Generated by database
-Name
-Description
-Property1
-Property2
-Property3
Entity Activity attributes (this entity contains fixed records -read only-, no records are inserted here on performing inserts):
-Id (PK) Generated by database
-Name
-Description
-Cost
Join table contains 2 columns, that is, the IDs of the above entities, that are primary and foreign keys at the same time.
I have no problems inserting entries which automatically EF creates join table TripActivities and add entries successfully to it. Also entries are added successfully to entity Trip and it leaves unchanged entity Activity.
My problem is on updating entries, for example, - suppose user can modify information related to a trip from the GUI - so I take all the info from this GUI and I perform the following steps to update the existing trip:
Trip trip = Context.Trips.Find(id); // Search for the appropriate trip to update from Id
trip.Name = ObtainNameFromGUI();
trip.Description = ObtainDescriptionFromGUI();
trip.Property1 = ObtainProperty1FromGUI();
trip.Property2 = ObtainProperty2FromGUI();
trip.Property3 = ObtainProperty3FromGUI();
trip.Activities = new List<Activity>();
// From the GUI user selects from a checkbox list the activities associated to the trip
// So we read its Ids and from those ids we fetch from database the activities to obtain
// the info related to each activity selected in the GUI. This is all done inside the
// below method.
List<Activity> activities = this.ObtainActivitiesSelectedFromGUI();
// If no activites selected (=null) I want EF automatically deletes the entries in the
// joined table for this trip. And of course, if there are activities selected, EF
// should update the respectives entries in the joined table for this trip with the new
// ones.
if (activites != null)
{
activities.ForEach(a =>
{
trip.Activities.Add(a);
});
}
context.Trips.Add(trip);
context.SaveChanges();<br><br>
By doing this I want EF updates all the entities related (except Activity as it has fixed entries, must be kept unchanged), that is, Trip and the joined table automatically but it does not work: a new trip is created and more entries in the joined table (The only thing that is working is that entity Activity is kept unchanged as I want).
How to achieve this? I have spent a lot of hours trying to do this but without success...
Thanks in advance.
EDIT:
I have removed line:
context.Trips.Add(trip);
Now the results are:
-Entity Trip is correctly updated, no new records added which is Ok.
-Entity Activity is kept unchanged which is Ok.
-Join table: The old records for current trip being updated are not updated, instead new records are inserted for the current trip which is not correct.
I have used a different approach for similar scenario that I faced, which works well with Detached Entities. What I ended up was finding out which entities were added and which ones deleted by comparing GUI(detached entity) values to the database values. Here is the sample code that I have used. The entities in play are RelayConfig and StandardContact which have many to many relationship
public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {
RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
.Where(r => r.Id == relayConfig.Id).SingleOrDefault();
context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);
List<StandardContact> addedExposedContacts =
exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
List<StandardContact> deletedExposedContacts =
dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();
StandardContact dbExposedContact = null;
addedExposedContacts.ForEach(exposedContact => {
dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
dbRelayConfig.StandardContacts.Add(dbExposedContact);
});
deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact);});
You will use something like this. Assuming that you will get the related objects from the UI and just you are going to update the same in the database, some thing like the following will work.
context.Products.Attach(product);
context.ObjectStateManager.ChangeObjectState(product, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductDescription, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductModel, System.Data.EntityState.Modified);
context.SaveChanges();
As you may see here, we are setting the EntityState as Modified which hints EF to perform update for the related tables too.
Please post back your queries or any issues that you may encounter in this implementation.
I have this simple code : (update value)
I'm trying to update column "c"
using (MaxEntities ctx = new MaxEntities())
{
aa orders = (from order in ctx.aa
select order).First();
orders.c = 22;
ctx.SaveChanges();
}
this is the table :
CREATE TABLE [dbo].[aa](
[a] [int] NULL,
[b] [int] NOT NULL,
[c] [int] NOT NULL
) ON [PRIMARY]
and values inside :
but i get an exception :
The property 'c' is part of the object's key information and cannot be modified.
I'm new to EF.
any help will be much appreciated.
The property 'c' is part of the object's key information and cannot be modified.
That's why you can't edit it. Maybe you need to add id column as a key with identity specified
As explained in another answer EF must uniquely identify every entity. If you don't have PK in the database, EF will infer some key. Key is considered as fixed so if EF inferred c as part of the key (and it did it because it uses all non-nullable non-binary columns) you cannot change its value. Moreover EF takes all tables without primary key as readonly so even if you remove c from the key in the designer and modify c value you will get another exception when you execute SaveChanges.
The reason for the second exception is in the way how EF describes model and the database. When EF inferred key, it did it only for description of your entities and for context's internal needs but not for description of the database. When EF tries to save changes it builds UPDATE statement from database description and without information about real database PK columns it will not be able to identify correct record for update (every update in EF can affect only single record - EF checks ROWCOUNT). This can be solved by cheating EF and updating its database description = by describing some column in the table description as primary key. This leads to multiple problems:
You must have some unique column in the database otherwise this method will not work.
You must edit EDMX manually (as XML) to add this change
You must not use default MS EDMX designer for updating your model from database because it will delete your change
Simple advice: Either use database tables with primary keys or don't use Entity framework.
Primary key missing here. Add primary key in table and it work.
I believe if there's no PK at all, EF uses all of the fields/columns as part of the key info.Here's a nice explanation: by #SteveWilkes of why. But what do your entities look like? The other possibility is that it doesn't have a property because the association is inside a different entity, if this is a foreign key.
EDIT
This got me thinking. There are just going to be situations where you have to work with legacy tables having no PK, even if you would never create such a thing. What about views? EF is a mapper - it has to uniquely identify that record so it infers and defines this key. Yes, you could use stored procedures, but could you also hack the XML and remove the keys from the table definition?
AND EDIT AGAIN
After posting this, I see #Ladislav Mrnka already said a similar idea (cheating EF and updating its database description), so it has been done (WARNING: Consume at your own risk - never tried). Quick google got me this blog with clear instructions:
Close the model designer in Visual Studio if it is still open and re-open the .edmx file in an XML editor
Find the edmx:StorageModels -> Schema -> Entity Container -> EntitySet element that refers to the table in question
On the EntitySet element, rename the store:Schema attribute to Schema
Remove the store:Name attribute altogether
Remove the opening and closing DefiningQuery tags and everything in between them
Save and close the .edmx file
But really, who doesn't like a PK? Can you not add an id?