I have an application where I am using Fluent NHibernate to talk to a SQLite database and saving objects. When I run the code below, all of the new items enter into the loop with an empty Guid (which is expected), but then once the SaveOrUpdate function runs, all of the new items all recieve the same Guid. I added session.flush() to see if I could flush the session and force a unique Guid...but no dice.
Any help would be appreciated!
My Mapping File
Id(x => x.Id).GeneratedBy.GuidComb().Unique();
The Code
public void SaveItems()
{
using (ISession session = SessionProvider.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
foreach (Item item in this.Items)
{
session.SaveOrUpdate(item);
session.Flush();
}
transaction.Commit();
}
}
}
Originally this was a comment, but since it turned out to be correct it should be an answer instead:
Verify that the members of the Items collection are truly distinct objects, and not just multiple references to the same instance.
Related
We save a list of records from one application by removing the list of items and saving the updated list (the list is always less than 10 items):
using (var context = GetContext())
{
EFConfiguration.SuspendExecutionStrategy = true;
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
context.ItemTable.RemoveRange(context.ItemTable.Where(d => d.ForeignKey == foreignKey));
foreach (var item in items)
{
SetTimeStampFields(item);
context.ItemTable.Add(item);
}
int i = await context.SaveChangesAsync();
dbContextTransaction.Commit();
EFConfiguration.SuspendExecutionStrategy = false;
}
catch (Exception)
{
dbContextTransaction.Rollback();
EFConfiguration.SuspendExecutionStrategy = false;
throw;
}
}
}
These changes show up in the database immediately. Then we pull the info from this table from another application:
var itemList = await context.ItemTable.ToListAsync();
The application seems to pickup the updated values when first deployed but afterwards they are stuck to the same values. I have tried adding AsNoTracking() before .ToListAsync() to try and circumvent any cache as this is readonly data but that did not work. The only solution that I have found that gets the data in real time is:
var itemList = await context.ItemTable.SqlQuery("Select * from TableName").ToListAsync();
The table in question holds records with a primary key, two foreign keys, and some timestamp data.
My question is why will the context not pickup the updated values from the table? This issue manifests in local and deployed environments. This is an ASP.NET Core application using Entity Framework 6. The database in question is a mature SQL Server 2012 database that is used across multiple applications without any issues such as this.
I am trying to create a layered MVC project but I am having an UPDATE problem in EF. I am getting the following error.
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
I have DAL and BusinessLayer. In DAL, I have the following code for UPDATE
public void Update(params T[] entities)
{
using (var context = new BorselDBEntities())
{
foreach (T entity in entities)
{
context.Entry(entity).State = EntityState.Modified;
}
context.SaveChanges();
}
}
and this is how I call the DAL from BusinessLayer
public void UpdateProduct(params Product[] products)
{
_productRepository.Update(products);
}
Why am I getting the error above and what could I do to fix it?
One common reason is that context.Entry(entity) fails to get the entity which you want to update.
When you're debugging, see if context.Entry(entity) returns the entity; easily done by putting it on a separate line and setting a breakpoint afer:
public void Update(params T[] entities)
{
using (var context = new BorselDBEntities())
{
foreach (T entity in entities)
{
var myEntity = context.Entry(entity);
myEntity.State = EntityState.Modified;
}
context.SaveChanges();
}
}
If it's not, you'll need to work back through your code and work out why it's not able to pick it up. Often this will be because the identity/primary key column is not set on 'entity'.
E.g. in an MVC application, if you have an Edit/update form, remember to have a
#Html.HiddenFor(model => model.Id)
It is necessary to call the Attach method on the DbSet for the entity you are updating before you can change it's State. The local DbContext needs to contain the Entity or it will not know what changes to track.
https://msdn.microsoft.com/en-us/library/gg696261(v=vs.103).aspx
My problem is when I want to update only one object in the database, every object in my list are updated in the database. I load the list with the same session and I can't make an other session to make my update because I got an error : illegal attempt to associate a collection with two open sessions nhibernate.
There is my code that I use to make the update.
private ISession session = NHibernateConnexion.OpenSession();
using (var transaction = session.BeginTransaction())
{
session.Update(item);
transaction.Commit();
}
Item is the object that I want to update.
The code that I use to load the entire list:
public IList<Item> RetrieveAll()
{
var result = from i in session.Query<Item>()
orderby i.EstActif descending
select i;
IList<Item> listeTemp = result.ToList();
return listeTemp;
}
Thank you!
You have 2 options:
Save in new session
What you doing is right, but the only thing is, you need the evict the entity from session using which you fetched the data. Do something like this
sessionWhichFetchedTheData.evict(item)
private ISession session = NHibernateConnexion.OpenSession();
using (var transaction = session.BeginTransaction()
{
session.Update(item);
transaction.Commit();
}
Save in current session but clear the session before saving
session.clear()
sessionThatFetchedTheData.SaveOrUpdate(item)
I have the following method:
public static void UpdatePpsTransaction(IEnumerable<PpsTransaction> ppsTransaction)
{
using (var context = PpsEntities.DefaultConnection())
{
foreach (var trans in ppsTransaction)
{
context.PpsTransactions.Attach(trans);
}
context.SaveChanges();
}
}
}
I was removing these records but I ended up creating a field IsProcessed which I am now setting to true. Now I am updating the records instead of deleting them, keeping them for record keeping.
Anyhow, I am not getting any errors but it is not updating the record.
Any suggestions?
You're not telling EF that you have made any changes, try to use the Entry method, on your Context:
public static void UpdatePpsTransaction(IEnumerable<PpsTransaction> ppsTransaction)
{
using (var context = PpsEntities.DefaultConnection())
{
foreach (var trans in ppsTransaction)
{
context.Entry(trans).State = EntityState.Modified;
}
context.SaveChanges();
}
}
This way, the entities will be attached to the context in a modified stated, so when you call SaveChanges(), these will be saved.
This will only work if the entities already exists in the database, which they should.
From msdn:
If you have an entity that you know already exists in the database but which is not currently
being tracked by the context then you can tell the context to track
the entity using the Attach method on DbSet. The entity will be in the
Unchanged state in the context. Note that no changes will be made to the
database if SaveChanges is
called without doing any other manipulation of the attached entity.
This is because the entity is in the Unchanged state.
Here is the link
I am new to Entity Framework so please bear with me.
I have a program that I want to select multiple records from a table and store it in a queue:
private Queue<RecordsToProcess> getRecordsToProcess()
{
Queue<RecordsToProcess> results = new Queue<RecordsToProcess>();
using (MyEntity context = new MyEntity())
{
var query = from v in context.RecordsToProcess
where v.Processed == false
select v;
foreach (RecordsToProcess record in query)
{
results.Enqueue(record);
}
}
}
Then I spin up multiple worker threads. Each worker thread takes one of the items in queue, processes it, and then saves it to the database.
private void processWorkerThread(object stateInfo)
{
while (workQueue.Count > 0)
{
RecordToProcess record = new RecordToProcess;
lock(workQueue)
{
if (workQueue.Count > 0)
RecordToProcess = workQueue.Dequeue();
else
break;
}
//Do the record processing here
//How do I save that record here???
}
}
My understanding is that to save changes back to the database you just call context.SaveChanges() but I can't do that in this situation can I?
Any help is appreciated.
Thanks!
Since you are disposing your MyEntity context in the first method (by wrapping it in a using statement), the entities that are enqueued will be in a "detached" state. That means, among other things, that changes done to the entity will not be tracked and you will not be able to lazy load navigation properties.
It is perfectly fine to dequeue these entities, "attaching" them to a different context, update them, and then call SaveChanges to persist the changes.
You can read about Attaching and Detaching Objects and Add/Attach and Entity States
It might be safer if you save off the primary key in the queue instead and retrieve the entities again. This way you are more likely avoid any data concurrency issues.