How can i insert object twice in database with different value
when the object User has Code > 10 then it insert 2 object but some how EF update the value of the first inserted object.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Code { get; set; }
}
and in my action i'm saving to databse
// Post Action
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
user.Name = "NAS";
uow.Users.Add(user);
uow.Commit(); //save second time
}
My object is
User = (Name = "Mike",Code=12)
Database result is
Result Database
id =1 Name="NAS" Code=12
id =2 Name="NAS" Code=12
This is correct behavior; so why is this happening:
When you save the first time, it creates a new entry in the database using your originally created user object.
When you save the second time, it takes the original object, which for lack of a better term, has a database reference and updates the name of the object AND it also adds that same object to the collection again; this is why you are seeing the same information in the database.
Your code should be:
var user = new User { Name = "Mike", Code = 12 };
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
var newUser = new User { Name = "NAS", Code = user.Code };
uow.Users.Add(newUser);
uow.Commit(); //save second time
}
Your results should then be:
Result Database
id = 1 Name = "Mike" Code = 12
id = 2 Name = "NAS" Code = 12
drneel gave a good explanation on why your code does not work as you intended.
Now maybe there is a workaround for what you want to achieve: inserting two distinct entities in DB from a single instance. Detach it from context, change what needs to be changed, re-add it.
If your uow is inheriting from DbContext, it would be:
var user = new User { Name = "Mike", Code = 12 };
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
uow.Entry(user).State = EntityState.Detached;
user.Id = 0;
user.Name = "NAS";
uow.Users.Add(user);
uow.Commit(); //save second time
}
I am not comfortable with the way we have to detach entities in EF. It may work better to instead dispose your uow after first 'save' and use another one for second 'save'.
The main risk concerns navigation properties. Detaching does not ends tracking on collection of other entities which may hold a reference to your user instance. Re-adding it may screw EF navigation properties bookkeeping.
(I would not fear that with NHibernate, such bookkeeping is not handled by NH. It remains the responsibility of the developer with NH.)
Related
Here is a simple entity:
public class Customer : Entity
{
public virtual Location Location { get; set; }
}
Now suppose we have a customer already:
var customer = new Customer() {Location = new Location("China")};
and now we want to update his location:
var customer = context.Customers.First(x => x.Location.Country == "China");
customer.Location = new Location("America");
context.SaveChanges();
and now when I look at the database, the location record "China" has not been deleted: the database now has two location records association with one customer record.
The reason for this issue is that I'm using virtual keyword on the Customer.Location property, and when I query the customer entity from database I didn't use Include method to load the Location property, and I also did not use any accesses to lazy load it. So EF can't track and know the China Location entity should be deleted.
I think the approach I used to the update virtual property is in line with intuition. I want update a property, then just use a update instruction "entity.xxx=...", add being forced to use some access of the property or method call while loading "entity.xxx" is not intuitive.
So I am looking for some better way to replace an entity's virtual property directly. Any suggestions?
Solutions update
I'm find two way to do this easy,
First you can use Identifying relation(recommend).
Another way you can Use ObjectContext.DeleteObject method, below is the example code:
public static class EFCollectionHelper
{
public static void UpdateCollection<T, TValue>(this T target,
Expression<Func<T, IEnumerable<TValue>>> memberLamda, TValue value)where T : Entity
{
var memberSelectorExpression = (MemberExpression)memberLamda.Body;
var property = (PropertyInfo)memberSelectorExpression.Member;
var oldCollection = memberLamda.Compile()(target);
oldCollection.ClearUp();
property.SetValue(target, value, null);
}
public static void ClearUp<T>(this IEnumerable<T> collection)
{
//Convert your DbContext to IObjectContextAdapter
var objContext = ((IObjectContextAdapter) Program.DbContext).ObjectContext;
for (int i = 0; i < collection.Count(); i++)
{
objContext.DeleteObject(collection.ElementAt(i));
}
}
}
And then you can simply write the code like:
customer.UpdateCollection(x => x.Locations, null);
Not completely sure what you want, but this is what i got.
The reason for you now getting two locations are because you use new Location("American"); you actually add a reference to a new location (EF don't know if China is used by another customer, and would never delete it, in that type of query)
Now if you said.
customer.Location.Country = "America"
The China would be overwritten by America, as we are now working with a specific Location's property.
Read the coments on the question so a little extras
If you want to update the location fully (new Location("Some new location")). Then you would do it like this.
Location oldLocation = customer.Location;
Location newLocation = new Location("America");
//Check if the new location country !exist
if(!context.Locations.Any(a=> a.Country == newLocation.Country))
{
//If it don't exist then add it (avoiding the location duplicate)
customer.Location = newLocation;
//If its important to delete the old location then do this
//(important to do after you removed the dependency,
//thats why its after the new location is added)
context.Locations.Remove(oldLocation)
//Finally Save the changes
context.SaveChanges();
}
Another way to update an entity is using the Entry.OriginalValues.SetValues method:
var currentLocation = context.Customers.First(x => x.Location.Country == "China").Select(c => c.Location);
context.Entry(currentLocation).OriginalValues.SetValues(newLocation) ;
context.SaveChanges();
I have the following code:
model = new Option();
model.val1 = newVal1;
model.val2 = newVal2;
model.val3 = newVal3;
//this saves new record just fine
if (recordCount < 1)
{
context.Options.AddObject(model);
context.SaveChanges();
}
else
{
var tempID = from s in context.Options where (s.val1 == newVal1 && s.val2 == newVal2) select s.ID;
var resultsID = tempID.First();
model = context.Options.Single(m => m.ID == resultsID);
if (TryUpdateModel(model, new[] { "val3" }))
{
//this isn't updating the record
context.SaveChanges();
}
}
The database adds a new entry just fine, but isn't updating it. What am I missing? Thanks.
Looking at this code, you first make a new model and set some properties on it:
model = new Option(); // <- A
model.val1 = newVal1;
model.val2 = newVal2;
model.val3 = newVal3;
then, assuming you're heading down the "else" path you do this:
var tempID = from s in context.Options where (s.val1 == newVal1 && s.val2 == newVal2) select s.ID;
var resultsID = tempID.First();
model = context.Options.Single(m => m.ID == resultsID); // <- B
if (TryUpdateModel(model, new[] { "val3" }))
{
//this isn't updating the record
context.SaveChanges();
}
which goes out and finds the entry in context.Options that has the matching ID.
So, now that model, which you created via the new() call (which I've marked with the comment "A") is now cast adrift and you've got a different one - the one you retrieved via the call to context.Options.Single(), which I've marked with the comment "B". It has properties based on what's in the context, not what was in that object you made. That A object is gone now. You've got a new object, B, retrieved from the DB.
So now, you're calling TryUpdateModel on this retrieved object, telling it that val3 is updated, but the value hasn't changed, right? It's whatever you pulled from the context.
So, it's not going to update anything because the model object isn't the one you think it is... the one you updated is waiting to be garbage collected. The one you retrieved hasn't been updated because it still has whatever value it's got for the property val3.
Assuming I follow what you're trying to do here, that's why you're not seeing any updated values in the context.
If you want to change the value of the val3 property on that model object you've retrieved, you need to set it after you retrieve it, otherwise it is overwritten.
If you are using a global context, you will have to update the context itself because it is not soft-link to the database.
context.SaveChanges();
DbContext context = new DbContext();
Check if Configuration.AutoDetectChangesEnabled = true;
I have simple entity:
public class Hall
{
[Key]
public int Id {get; set;}
public string Name [get; set;}
}
Then in the Seed method I use AddOrUpdate to populate table:
var hall1 = new Hall { Name = "French" };
var hall2 = new Hall { Name = "German" };
var hall3 = new Hall { Name = "Japanese" };
context.Halls.AddOrUpdate(
h => h.Name,
hall1,
hall2,
hall3
);
Then I run in the Package Management Console:
Add-Migration Current
Update-Database
It's all fine: I have three rows in the table "Hall". But if I run in the Package Management Console Update-Database again I have already five rows:
Id Name
1 French
2 Japaneese
3 German
4 French
5 Japanese
Why? I think it is should be three rows again, not five. I tried to use Id property instead of Name but it does not make the difference.
UPDATE:
This code produces the same result:
var hall1 = new Hall { Id = 1, Name = "French" };
var hall2 = new Hall { Id = 2, Name = "German" };
var hall3 = new Hall { Id = 3, Name = "Japanese" };
context.Halls.AddOrUpdate(
h => h.Id,
hall1);
context.Halls.AddOrUpdate(
h => h.Id,
hall2);
context.Halls.AddOrUpdate(
h => h.Id,
hall3);
Also I have the latest EntityFramework installed via nuget.
Ok I was banging my face off the keyboard for an hour with this. If your table's Id field is an Identity field then it won't work so use a different one for identifierExpression. I used the Name property and also removed the Id field from the new Hall {...} initializer.
This tweak to the OPs code worked for me so I hope it helps someone:
protected override void Seed(HallContext context)
{
context.Halls.AddOrUpdate(
h => h.Name, // Use Name (or some other unique field) instead of Id
new Hall
{
Name = "Hall 1"
},
new Hall
{
Name = "Hall 2"
});
context.SaveChanges();
}
I know this is an old question, but the right answer is that if you are setting the id # yourself and you want to use AddOrUpdate then you need to tell EF/SQL that you don't want it to generate the ID #.
modelBuilder.Entity<MyClass>().Property(p => p.Id)
.HasDatabaseGeneratedOption(System.ComponentModel
.DataAnnotations.Schema.DatabaseGeneratedOption.None);
The down side to this is that when you insert a new item you need to set it's Id, so if this is done dynamically at runtime (instead of from seed data) then you will need to calculate out the next Id. Context.MyClasses.Max(c=>c.Id) + 1 works well.
This code works:
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(HallContext context)
{
context.Halls.AddOrUpdate(
h => h.Id,
new Hall
{
Id = 1,
Name = "Hall 1"
},
new Hall
{
Id = 2,
Name = "Hall 2"
});
context.SaveChanges();
}
This can also be caused if you're setting the Entity State incorrectly. I kept getting the following error when I'd run update-database..."Sequence contains more than one matching element."
For example, I had duplicate rows being created on each update-database command (which of course is not supposed to happen when seeding data), and then the next update-database command wouldn't work at all since it found more than one match (hence the sequence error saying I have more than one matching row). That's because I had overridden SaveChanges in my context file with a method call to ApplyStateChanges...
public override int SaveChanges()
{
this.ApplyStateChanges();
return base.SaveChanges();
}
I was using ApplyStateChanges to ensure that when adding object graphs, Entity Framework knows explicitly whether the object is in an added or modified state. The entire explanation on how I'm using ApplyStateChanges can be found here.
And this works great (but the caveat!!)...if you're also seeding the database using CodeFirst migrations, then the above method will cause havoc for the AddOrUpdate() call within the Seed Method. So before anything else, just check your DBContext file and ensure you're not overriding SaveChanges in the way above, or you will end up getting duplicate data running the update-database command a second time, and then won't work at all the third time since there's more than one row for each matching item.
When it comes down to it, you don't need to configure the Id in AddOrUpdate()...that defeats the whole purpose of easy and initial database seeding. It works fine by something like:
context.Students.AddOrUpdate(
p => p.StudentName,
new Student { StudentName = "Bill Peters" },
new Student { StudentName = "Jandra Nancy" },
new Student { StudentName = "Rowan Miller" },
new Student { StudentName = "James O'Dalley" },
just AS LONG as I'm not overriding the SaveChanges method in my context file with a call to ApplyStateChanges. Hope this helps.
These steps worked for me
Delete all the rows in the table.
Reset the incremental identity to 0. DBCC CHECKIDENT (yourtablename, RESEED, 0) (The primary keys specified in the Seed() must match those in the database table so that they do not duplicate.)
Specify the primary keys in the 'seed' method.
Run the Seed() method several times and you check if they duplicated.
I have found that AddOrUpdate works fine with fields that are not ID's. If this works for you: context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)
You may want to use Hall names like 'French_test_abc_100', 'German_test_abc_100' etc.
That stops hard coded test data messing things up when you are testing your app.
If object(hall)'s id is 0, it is a insertion. I think you need to double check the id field of your hall objects
Is your ID field an Identity field? I was running into this same issue. When I removed the Identity status from my ID field and set the IDs going into the database, that resolved the issue.
That worked for me, since these were look-up tables and shouldn't have been identity fields, anyway.
I used the ID field as Identity/Key and add attributes not to assign Ids by the server. This solved the problem for me.
public class Hall
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id {get; set;}
public string Name [get; set;}
}
Just to Ciaren's answer, the below code of resetting the context on ModelCreating, helped me resolve similar issues. Make sure change "ApplicationContext" to your DbContext name.
public class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext() : base("ApplicationContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Database.SetInitializer<ApplicationContext>(null);
base.OnModelCreating(modelBuilder);
}
}
I found out that for this to work, the identity position should be 0 when the seed first run. You can reset it using:
DBCC CHECKIDENT (tableName, RESEED, 0)
I think it's likely that you need to back out existing database migrations (i.e. start your database from scratch) with something like Update-Database TargetMigration:0 followed by Update-Database.
As it is, you're not dropping the existing table or values, you're just add/updating those values. That needs to happen in order to get your desired result.
You could have also done this:
context.Halls.AddOrUpdate(new Hall[]{hall1,hall2, hall3});
There's a many-to-many UserFeed table that stands between User and Feed and denotes a twitter-like follow relationship.
It only has two fields, which form a composite key: UserID and FeedID.
I need to write a method that will subscribe or unsubscribe a user from a feed based on a boolean flag.
public void SetSubscriptionFlag (int userId, int storeId, bool subscribe)
{
}
I'm new to Entity Framework so I'm trying to find and follow an "EF-ish" way to accomplish this.
My initial thoughts are:
Instead of working with the middle UserFeed class, I should create a many-to-many Subscriptions property (EDIT: hit limitations here);
After I've done so, I'll need to fetch a User instance by ID, check whether it has given Feed in its Subscriptions and add/delete it depending on the flag and current existence;
Figure out how to avoid racing conflicts when there is a time interval before the check and adding/deleting and user manages to submit two adding or deletion requests;
Optimize my code as to avoid unneccessary SELECTs, if any occur, because all I really want to do is a single SELECT and single INSERT/DELETE.
A relevant code snippet and comment on my points is highly appreciated.
Thanks!
You can use dummy objects - it definitely works for insert and I hope it can be used for delete as well:
Create new relation:
var user = new User() { Id = userId };
context.Users.Attach(user);
var store = new Store() { Id = storeId };
context.Stores.Attach(store);
// Now context trackes both entities as "existing"
// and you can build a new relation
user.Subscriptions.Add(store);
context.SaveChanges();
Remove existing relation:
var user = new User() { Id = userId };
var store = new Store() { Id = storeId };
user.Subscriptions.Add(store);
context.Users.Attach(user);
// Now context trackes both entities as "existing"
// with "existing" relation so you can try to remove it
user.Subscriptions.Remove(store);
context.SaveChanges();
I have a DB like this that I generated from EF:
Now I'd like to add a "fielduserinput" entity so I write the following code:
public bool AddValueToField(string field, string value, string userId)
{
//adds a value to the db
var context = new DBonlyFieldsContainer();
var fieldSet = (from fields in context.fieldSet
where fields.fieldName.Equals(field)
select fields).SingleOrDefault();
var userSet = (from users in context.users
where users.id.Equals(userId)
select users).SingleOrDefault();
var inputField = new fielduserinput { userInput = value, field = fieldSet, user = userSet };
return false;
}
Obviously it's not finished but I think it conveys what I'm doing.
Is this really the right way of doing this? My goal is to add a row to fielduserinput that contains the value and references to user and field. It seems a bit tedious to do it this way. I'm imagining something like:
public bool AddValueToField(string userId, string value, string fieldId)
{
var context = new db();
var newField = { field.fieldId = idField, userInput = value, user.id = userId }
//Add and save changes
}
For older versions of EF, I think you're doing more or less what needs to be done. It's one of the many reasons I didn't feel EF was ready until recently. I'm going to lay out the scenario we have to give you another option.
We use the code first approach in EF 4 CTP. If this change is important enough, read on, wait for other answers (because Flying Speghetti Monster knows I could be wrong) and then decide if you want to upgrade. Keep in mind it's a CTP not an RC, so considerable changes could be coming. But if you're starting to write a new application, I highly recommend reading some about it before getting too far.
With the code first approach, it is possible to create models that contain properties for a reference to another model and a property for the id of the other model (User & UserId). When configured correctly setting a value for either the reference or the id will set the id correctly in the database.
Take the following class ...
public class FieldUserInput{
public int UserId {get;set;}
public int FieldId {get;set;}
public virtual User User {get;set;}
public virtual Field Field {get;set;}
}
... and configuration
public class FieldUserInputConfiguration{
public FieldUserInputConfiguration(){
MapSingleType(fli => new {
userid = fli.UserId,
fieldid = fli.FieldId
};
HasRequired(fli => fli.User).HasConstraint((fli, u)=>fli.UserId == u.Id);
HasRequired(fli => fli.Field).HasConstraint((fli, f)=>fli.FieldId == f.Id);
}
}
You can write the code...
public void CreateField(User user, int fieldId){
var context = new MyContext();
var fieldUserInput = new FieldUserInput{ User = user, FieldId = fieldId };
context.FieldUserInputs.Add(fieldUserInput);
context.SaveChanges();
}
... or vice versa with the properties and everything will work out fine in the database. Here's a great post on full configuration of EF.
Another point to remember is that this level of configuration is not necessary. Code first is possible to use without any configuration at all if you stick to the standards specified in the first set of posts referenced. It doesn't create the prettiest names in the database, but it works.
Not a great answer, but figured I'd share.