Entity Framework Insert bug? - entity-framework-6

I used EF 6.2.0 DB first, SQL Server 2017
I created a table to test this
create table Person
(
ID int primary key,
Name varchar(50)
)
And I created a form to insert, this is button click event:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
Person p = new Person();
p.id = Convert.ToInt32(txbID.Text);
p.name = txbName.Text;
try
{
db.People.Add(p);
db.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine("###Ex:" + ex.ToString());
MessageBox.Show(ex.ToString());
}
}
First, I insert a person with ID = 1.
Then, I insert another person with ID = 1 and it caused this exception:
System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK__Person__3213E83F397C4503'. Cannot insert duplicate key in object 'dbo.Person'. The duplicate key value is (1).
Finally, I insert a person with ID = 2 and it still show the same exception:
System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK__Person__3213E83F397C4503'. Cannot insert duplicate key in object 'dbo.Person'. The duplicate key value is (1).
After the first exception, insert any ID will cause the same exception The duplicate key value is (1). I think it's a bug.

No, it's not a bug - I suspect you haven't really understood how the DbContext works.
When you try to insert the second person with ID = 1 which will obviously create the error, that object (that causes this error) is now part of the DbContext (of the db.People collection).
If you add another person, with ID = 2, your "problematic" second person with ID=1 is still part of the DbContext (db.Person) - unless you've specifically cleaned up (removed that troublesome person, or created a new DbContext altogether).
So after adding the person with ID = 2, your DbContext now has a Person with ID=1 and another one with ID=2 to be saved when calling .SaveChanges() - and that will OF COURSE again fail with the same error - it's the same problem as before......
One way to solve this would be to explicitly create the DbContext inside your btnSubmit_Click method:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
using (var db = new YourDbContextType())
{
Person p = new Person();
p.id = Convert.ToInt32(txbID.Text);
p.name = txbName.Text;
try
{
db.People.Add(p);
db.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine("###Ex:" + ex.ToString());
MessageBox.Show(ex.ToString());
}
}
}

Related

Repository pattern giving exception while updating record

In my MVC application, I have been using Repository pattern for DAL.
Now, when I do select one entity record and and update the entity field value and do Update operation then getting below error.
Attaching an entity of type 'DAL.User' failed because another entity
of the same type already has the same primary key value. This can
happen when using the 'Attach' method or setting the state of an
entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate."} System.Exception
Below is repository stuff:
public void Update(TEntity entity)
{
if (_context.Entry(entity).State != EntityState.Modified)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
Calling as follow:
In Bussines layer library:
Manager class :
private readonly IUnitOfWork _unitOfWork;
private IRepository <User , int> UserRepository
{
get
{
return _unitOfWork.GetRepository<AccountUser, int>();
}
}
public void UpdateUserEntity(UserDTO u)
{
try
{
User model = new User ();
UserRepository.Update(Mapper.Map(u, model));
_unitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
Please guide me how I could resolve above error.
The exception says that there is another entity with the same key that has been attached, but different reference.
The exception could be caused by previous attached entity.
db.Set<Entity>().Attach(new Entity { Id = 123 });
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
Or could be also caused by tracked entity that automatically attached.
db.Set<Entity>().FirstOrDefault(e => e.Id == 123); // automatically attached
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
The second cause can be solved by mentioning AsNoTracking when retrieving item.
db.Set<Entity>().AsNoTracking().FirstOrDefault(e => e.Id == 123);
Or to be safe you can use this extension to always detach any attached entity.
public static class DbSetExtension
{
public static void SafeAttach<T>(
this DbContext context,
T entity,
Func<T, object> keyFn) where T : class
{
var existing = context.Set<T>().Local
.FirstOrDefault(x => Equals(keyFn(x), keyFn(entity)));
if (existing != null)
context.Entry(existing).State = EntityState.Detached;
context.Set<T>().Attach(entity);
}
}
Usage.
db.SafeAttach(entity, e => e.Id);
It's because of the reason,
"TEntity entity as a new object instead of the one which already exists".
Means,Entity framework treats each new object as new entry.(eventhough with same existing old data,PK & all).
Solution is,
First retrieve the object from database
Do/assign the changes to the same object (preferably without changing Primary key)
Then do state as Modified ,Update,SaveChange()

How to get all values in a column in table using vaadin SQLContainer

Hi Stackoverflow members,
I have one SQLContainer with TableQuery. Now, I want to get all the values in one column. How can I get it? Or Shall I need to go with FreefromQuery?
This is my SQLContainer code
public SQLContainer getMyContainer() {
//FreeformQuery query = null;
SQLContainer container = null;
try {
connectionPool = new SimpleJDBCConnectionPool(
"com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/vaadin", "root", "root",
2, 5);
TableQuery query = new TableQuery("components", connectionPool);
/*query = new FreeformQuery(
"SELECT rowNum, colNum, caption, cType FROM items where screenId='"
+ screenName+"'", connectionPool);
query.setDelegate(new DemoFreeformQueryDelegate());*/
container = new SQLContainer(query);
} catch (Exception e) {
}
return container;
}
It depends on what you wish to do.
With SQLContainer you could do it this way:
// Returns a list with all ID's (Beware of large resultsets)
Collection<?> itemIDS= conatiner.getItemIds();
for (Object itemID : itemIDS)
{
Property property= container.getContainerProperty(itemID, "COLUMN");
Object data= property.getValue();
}
The getItemIds() must fetch all rows from the DB (at least the primary keys),
so this will cause troubles with large resultsets/tables.
https://vaadin.com/api/com/vaadin/data/util/sqlcontainer/SQLContainer.html#getItemIds%28%29

Lambda expression for getting a table row and update the row in database

I am trying to update my DB table and from my model I am getting the id
public void Update(Abc model)
{
//Database Table Instance
Abc a=new Abc();
//Trying to update the column where id =id
try
{
a.id = model.Asset.id;
a.column = "R";
db.Entry(a).State = EntityState.Modified;
db.SaveChanges();
}
catch (Exception e) { }
}
It set the column to R bt sets Nulls for all other columns because I am not getting values for other columns from model. One way is to set the hidden values in model and send the model but the table has about 30 columns. so I want to get the table row from database where id=id and the only updatte the column in that row in my function in repository .....
In order to achieve this first you need to get the actual object from your db context and then change the desired property and save it to in database like below:
try
{
Abc a = db.Abcs.SingleOrDefault(a => a.id == model.Asset.id);
a.column = "R";
db.SaveChanges();
}
catch (Exception e) { }
This will fix your concern.

Asp.Net MVC 3 - Linq To Entities - PK with Null doesn't get inserted into the db (don't want null :))

I'm using the latest Asp.Net MVC version.
For some reason, when my POST (Action Create) in my controller gets hit.
I can't seem to be able to add it to the entityset.
What i have is,
1) My EntityModel (*.edmx file)
2) Controller which references the entity:
private db.DataContainer _db = new db.DataContainer();
3) My method (i'm using Guid as pk):
[HttpPost]
public ActionResult Create(Client client)
{
try
{
client.Id = Guid.NewGuid();
/* method 2
Client cl = new Client();
cl.Id = Guid.NewGuid();
cl.email = client.email;
cl.Adres = client.Adres;
cl.companyName = client.companyName;
cl.fax = client.fax;
cl.phone = client.phone;
*/
// client.Id = Guid.NewGuid();
_db.ClientSet.AddObject(client);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
var ex_message = ex.Message;
var ex_data = ex.Data;
var ex_ix = ex.InnerException;
return View();
}
}
4) Following is my InnerException:
[System.Data.SqlClient.SqlException] = {"Cannot insert the value NULL into column 'Id', table 'lst.dbo.ClientSet'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}
Both doesn't seem to work :(
GUIDs are not supported as primary keys in the Entity Framework. You will need to modify your save method to generate a new GUID for your added objects http://msdn.microsoft.com/en-us/library/dd283139.aspx
It seems that changing my "saveCommand" has given my a temporarily solution:
I chaned:
_db.SaveChanges()
To
_db.SaveChanges(System.Data.Objects.SaveOptions.None);

Can not update object in control action with ASP.NET MVC?

I have a view with a form, when user submit the form, the matched action method is like:
public ActionResult Test(ViewModel vm, Member member)
{
//...
if (ModelState.IsValid)
{
try{
//...
member.OID = 1; //error here
//...
}Catch(Exception ex)
{
//...
}
}
}
It works fine before, but now I get error as below when assigning the value to a object property:
Operation is not valid due to the current state of the object
why? how to resolve it?
It is not very clear as your code sample, if my guess is right. Member object is LINQ's class where you have OID as a FK to other object in your schema.
The error show that you cannot assign OID directly. say your O is Occupation Id. then you have to
member.Occupation = (from c in dc.Occupation where c.ID = 1 select c);
Hope this helps.

Resources