data context save changes not working in C# entity framwork - c#

I am trying to execute below query
using (var dbcontext = new EVEntities())
{
var data_header = dbcontext.Cl.Where(x => x.PKey ==
header_key).FirstOrDefault();
if (data_header != null)
{
data_header.EstimatedCost = Math.Round(estimated_cost,2);
data_header.ClaimedCost = Math.Round(claimed_cost,2);
dbcontext.Entry<Cl>(data_header).State = System.Data.Entity.EntityState.Modified;
dbcontext.SaveChanges();
Writelog("Updated");
}
}
Here Writelog write in a text file and it is working always. But the field in Cl is not getting updated. In between the data is getting updated also.
Connection String
<connectionStrings><add name="EVEntities" connectionString="metadata=res://*/xxx_Entity_Model.csdl|res://*/xxx_Entity_Model.ssdl|res://*/xxx_Entity_Model.msl; provider=System.Data.SqlClient;provider connection string="data source=xxxxxx;initial catalog=xxxxx;persist security info=True;user id=xx;password=xxxxx;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient"/></connectionStrings>
Edit1
tried raw update also
dbcontext.Database.ExecuteSqlCommand(#"UPDATE dbo.Claims
SET EstimatedCost = #e_cost, ClaimedCost = #c_cost WHERE Pkey =
#p_key",
new SqlParameter("e_cost", Math.Round(estimated_cost, 2)),
new SqlParameter("c_cost", Math.Round(claimed_cost, 2)),
new SqlParameter("p_key", claim_header_key));
same outcome. It get updated in between. No error.

You didn't give us enough information to give you the solution. Therefore I give you a method to debug the problem.
Does the DbContext think that anything must be saved?
What SQL is sent to the database?
When you call SaveChanges, DbContext checks its ChangeTracker to see if anything must be updated. Consider to write some Debug code to detect whether there are changes.
Insert just before SaveChanges:
bool changesDetected = dbContext.ChangeTracker.HasChanges;
It might be that you need to call DetectChanges() first. I'm not sure.
If there are Changes, check if the item that you think that should be updated is changed:
IEnumerable <DbEntityEntry<Cl>> entries = dbContext.ChangeTracker.Entries<Cl>;
// We're expecting exactly one entry:
DbEntityEntry<Cl> myEntry = entries.SingleOrDefault();
Assert(myEntry != null);
If null, try to find out why it is not tracked. Was it tracked after you fetched it, before you changed it? Do you have somewhere tracking switched off? Write some other debug code where you fetch some other data. Is that tracked?
If not null, then apparently your Cl is tracked. It ought to be changed:
Assert(myEntry.State == EntityState.Modified);
If not modified, fetch the original values and the current values:
DbPropertyValues originalValues = myEntry.OriginalValues;
DbPropertyValues currentValues = myEntry.currentValues;
In your debugger, check them, or write some debug code to compare the original value with the current values. Are the changed values correct?
I'm not sure if entity framework will try to update objects that are unmodified and of which the original values are not equal to the current values. We'll find out to see what SQL is created when you do the SaveChanges.
It would be nice if your database can log all communications.
You can also log what entity framework sends to your database. For this, use property DbContext.Database.Log. For example:
dbContext.Database.Log = Console.Write;
dbContext.SaveChanges();
If you can't write to Console, write a method:
private List<string> SqlCommands {get;} = new List<string>();
void LogSqlCommands(string sqlCommand)
{
this.SqlCommands.Add(sqlCommand);
}
And in your method that following debug code:
using (var dbcontext = new EVEntities())
{
this.SqlCommands.Clear();
dbContext.Database.Log = this.LogSqlCommands;
var data_header = ... etc
dbContext.SaveChanges();
}
Put a breakpoint after SaveChanges and check the generated SQL.
Hope these debugging tips help you to find the cause of your problem

Related

RavenDB is not tracking entity changes

I have the following code that opens a session with RavenDB, gets the relevant IDs, uses those ideas to load the entities, change them, and finally save them.
List<EventDescriptor> events;
using (var session = raven.OpenSession())
{
session.Store(aggregate);
session.SaveChanges();
events = (from descriptor in session.Query<EventDescriptor>() where descriptor.AggregateId == aggregate.Id select descriptor).ToList();
}
using (var session = raven.OpenSession())
{
foreach (var #event in events)
{
var e = session.Load<EventDescriptor>("EventDescriptors/" + #event.Id.ToString());
e.Saved = true;
}
session.SaveChanges();
}
The problem however is that the changes in the entities don't seem to be tracked, and I can't delete the entities either (gives me unknown entity error), even though the object is loaded. I already tried calling SaveChanges inside the loop, but that didn't help either. I looked at the Raven documentation but I don't see what I'm doing wrong here.
Yes, we can't track changes on structs, because every time that you change them, you create a new copy
The problem was that EventDescriptor was a struct, and not a class. Changing this solved the problem. I assume it's because a struct is a valuetype and not a referencetype.

Nhibernate transaction locks a tabel

I have developed a WCF api which is using nHibernate. I am new to this. I have used session.update to take care of transaction. I have a for loop in which based on select condition I am updating a record ie. If A is present in tabel1 then I am updating the table else inserting a new entry.
I am getting "could not execute query." when trying to execute a select query on a table which was previously being updated by adding a new entry in the table.
What I think is, because I am using session.save(table1) and then trying select entries from that table I am getting an error. Since session.save temporarily locks the table I am not able to execute a select query on that table.
What can be the solution on this?
Update:
This the for loop I am using to check in the database for some field:
using (ITransaction tranx = session.BeginTransaction())
{
savefunction();
tranx.Commit();
}
Save function:
public void savefunction()
{
for (int i = 0; i < dictionary.Count; i++)
{
ICandidateAttachmentManager candidateAttach = new ManagerFactory().GetCandidateAttachmentManager();
CandidateAttachment attach = new CandidateAttachment();
attach = checkCV();
if(attach == null)
{
//insert new entry into table attach
session.save(attach);
}
}
}
checkCV function:
public void checkCV()
{
using (ICandidateAttachmentManager CandidateAttachmentManager = new ManagerFactory().GetCandidateAttachmentManager())
{
IList<CandidateAttachment> lstCandidateAttachment = CandidateAttachmentManager.GetByfkCandidateId(CandidateId);
if (lstCandidateAttachment.Count > 0)
{
CandidateAttachment attach = lstCandidateAttachment.Where(x => x.CandidateAttachementType.Id.Equals(FileType)).FirstOrDefault();
if (attach != null)
{
return null;
}
else
{
return "some string";
}
}
}
}
What happening here is in the for loop if say for i=2 the attach value comes to null that I am entering new entry into attach table. Then for i=3 when it enters checkCV function I get an error at this line:
IList lstCandidateAttachment =
CandidateAttachmentManager.GetByfkCandidateId(CandidateId);
I think it is because since I am using session.save and then trying to read the tabel contents I am unable to execute the query and table is locked till I commit my session. Between the beginTransaction and commit, the table associated with the object is locked. How can I achieve this? Any Ideas?
Update:
I read up on some of the post. It looks like I need to set isolation level for the transaction. But even after adding it doesn't seem to work. Here is how I tried to inplement it:
using (ITransaction tranx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
saveDocument();
}
something I don't understand in your code is where you get your nHibernate session.
Indeed you use
new ManagerFactory().GetCandidateAttachmentManager();
and
using (ICandidateAttachmentManager CandidateAttachmentManager = new ManagerFactory().GetCandidateAttachmentManager())
so your ManagerFactory class provides you the ISession ?
then you do:
CandidateAttachment attach = new CandidateAttachment();
attach = checkCV();
but
checkCV() returns either a null or a string ?
Finally you should never do
Save()
but instead
SaveOrUpdate()
Hope that helps you resolving your issue.
Feel free to give more details

Not able to retrieve OriginalValues in Entity Framework 5

I am writing a asp.net mvc4 app and I am using entity framework 5. Each of my entities have fields like EnteredBy, EnteredOn, LastModifiedBy and LastModifiedOn.
I am trying to auto-save them by using the SavingChanges event. The code below has been put together from numerous blogs, SO answeres etc.
public partial class myEntities : DbContext
{
public myEntities()
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.SavingChanges += new EventHandler(context_SavingChanges);
}
private void context_SavingChanges(object sender, EventArgs e)
{
ChangeTracker.DetectChanges();
foreach (ObjectStateEntry entry in
((ObjectContext)sender).ObjectStateManager
.GetObjectStateEntries
(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
CurrentValueRecord entryValues = entry.CurrentValues;
if (entryValues.GetOrdinal("LastModifiedBy") > 0)
{
HttpContext currContext = HttpContext.Current;
string userName = "";
DateTime now = DateTime.Now;
if (currContext.User.Identity.IsAuthenticated)
{
if (currContext.Session["userId"] != null)
{
userName = (string)currContext.Session["userName"];
}
else
{
userName = currContext.User.Identity.Name;
}
}
entryValues.SetString(
entryValues.GetOrdinal("LastModifiedBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("LastModifiedOn"), now);
if (entry.State == EntityState.Added)
{
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), now);
}
else
{
string enteredBy =
entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy"));
DateTime enteredOn =
entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn"));
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"),enteredBy);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), enteredOn);
}
}
}
}
}
}
My problem is that entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy")) and entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn")) are not returning the original values but rather the current values which is null. I tested with other fields in the entity and they are returning the current value which were entered in the html form.
How do I get the original value here?
I think the problem may be that you are using the instance provided by the model binder as the input to your controller method, so EF does not know anything about that entity and its original state. Your code may look like this:
public Review Update(Review review)
{
_db.Entry(review).State = EntityState.Modified;
_db.SaveChanges();
return review;
}
In that case, EF knows nothing about the Review instance that is being saved. It is trusting you and setting it as modified, so it will save all of its properties to the database, but it does not know the original state\values of that entity.
Check the section named Entity States and the Attach and SaveChanges Methods of this tutorial. You can also check the first part of this article, that shows how EF does not know about the original values and will update all properties in the database.
As EF will need to know about the original properties, you may first load your entity from the database and then update its properties with the values received in the controller. Something like this:
public Review Update(Review review)
{
var reviewToSave = _db.Reviews.SingleOrDefault(r => r.Id == review.Id);
//Copy properties from entity received in controller to entity retrieved from the database
reviewToSave.Property1 = review.Property1;
reviewToSave.Property2 = review.Property2;
...
_db.SaveChanges();
return review;
}
This has the advantage that only modified properties will be send and updated in the database and that your views and view models don't need to expose every field in your business objects, only those that can be updated by the users. (Opening the door for having different classes for viewModels and models\business objects). The obvious disadvantage is that you will incur an additional hit to the database.
Another option mentioned in the tutorial I referenced above is for you to save the original values somehow (hidden fields, session, etc) and on save use the original values to attach the entity to the database context as unmodified. Then update that entity with the edited fields. However I would not recommend this approach unless you really need to avoid that additional database hit.
Hope that helps!
I was running into a similar problem when trying to audit log the Modified values of an Entity.
It turns out during the post back the ModelBinder doesn't have access to the original values so the Model received is lacking the correct information. I fixed my problem by using this function which clones the current values, relods the object, and then reset the current values.
void SetCorrectOriginalValues(DbEntityEntry Modified)
{
var values = Modified.CurrentValues.Clone();
Modified.Reload();
Modified.CurrentValues.SetValues(values);
Modified.State = EntityState.Modified;
}
You can gain access to the DbEntityEntry though the change tracker, or the entry function from your context.

nhibernate use_identifier_rollback all-delete-orphan

In NH is a configuration setting 'use_identifier_rollback' which is sets the id of an entity back to its default value.
This settings works with every cascade options except 'delete-orphan'. (And I know why!)
Take a look at C# Identifier Rollback
Take a look at Java Identifier Rollback
// Works with 'use_identifier_rollback' and 'cascade-option=all'
// but not with 'cascade-option=all-delete-orphan'
Sample sample = new Sample("sample");
sample.Add(new Subsample("subsample");
int sampleId;
using(var session = sessionFactoy.OpenSession())
{
using(var tx = session.BeginTransaction())
{
session.Save(sample);
sampleId = sample.Id;
Assert.That(sampleId, Is.GreaterThan(0));
Assert.False(sample.IsTransient)
} // Rollback
}
Assert.That(sample.Id, Is.EqualTo(0));
Assert.True(sample.IsTransient)
Is it bad practice to revert the id when rollbacking the save?
In the java code is not comment out and works.
UPDATE:
What behavior do you usually expect when you delete an entity?
// Works with 'use_identifier_rollback' and 'cascade-option=all'
// but not with 'cascade-option=all-delete-orphan'
int sampleId; // sampleId from above
using(var session = sessionFactoy.OpenSession())
{
using(var tx = session.BeginTransaction())
{
Sample sample = session.Get<Sample>(sampleId);
Assert.That(sampleId, Is.GreaterThan(0));
Assert.False(sample.IsTransient)
session.Delete(sample);
tx.Commit();
}
}
Assert.That(sample.Id, Is.EqualTo(0));
Assert.True(sample.IsTransient)
With 'use_identifier_rollback' nhibernate sets the id to '0' or more exactly to the default of the identity. My IsTransient property depends on Id == 0
How do you handle entities when they becomes deleted, in case of is the entity transient or what is the Id of a deleted entity, ...?
Looks like it just hasn't been implemented yet in NHibernate. See NHibernate's issue tracker: NH-387. Log in and vote for the issue if it's important to you. You can even contribute unit tests and fixes via GitHub. Looks like someone has already done some work on this issue - there's a couple of patch files attached to the issue, but I don't know what state they're in.

Lazy Loading and multiple references

My Database is set up with an Entity table, which has a Ref_Type navigation property (and a FK which references TypeID). The Type table, has a Ref_Department, same FK setup. Finally, the Department table has a Ref_Locale and same FK setup.
I save the Entities in a List, which is a property of a singleton. It is created as follows;
private Singleton()
{
using (Database db = new Database())
{
List<Entities> EntityList = db.Entities.ToList<Entities>();
}
}
This is fine, the navigation properties are ALL loaded and I can access any one of them.
The problem comes when I update an Entity entry as follows;
public void UpdateEntity(Entities oldEnt, Entities newEnt)
{
using (Database db = new Database())
{
Entities ent = db.Entities.Where(e => e.EntityName == oldEnt.EntityName).FirstOrDefault();
ent.EntityName = newEnt.EntityName;
ent.EntityEmail = newEnt.EntityEmail;
...
ent.EntityType_ID = newEnt.EntityType_ID;
db.SaveChanges();
}
RefreshEntities();
}
public void RefreshEntities()
{
using (Database db = new Database())
{
db.Configuration.LazyLoadingEnabled = false;
db.SaveChanges();
EntityList = db.Entities.Include("Ref_EntityType").Include("Ref_EntityPosition").ToList<Entities>();
}
}
Ref_Entity gets loaded properly, but then within Ref_Entity, Ref_Department is just null. I've tried just using db.Entities.ToList<Entities>(); like in my constructor, no dice. As you can see, I've also tried turning LazyLoading off (I thought I might need to call SaveChanges() for it to actually apply the flag). I've also tried .Include("Ref_Department") but it just complains that it doesn't exist for Entities, which makes sense.
The newEnt that I pass to the UpdateEntity method does not have Ref_Type initialised, I'm working under the assumption that anything not changed in the UpdateEntity method would just stay the same...
So now I'm at a bit of a loss as to what's going on and how to fix it. If anyone could help explain where I'm going wrong or give me some pointers about how to fix my code to make it work, that would be great.
On a whim, I modified RefreshEntities() to;
EntityList = db.Entities.Include("Ref_EntityPosition").Include("Ref_EntityType").
Include("Ref_EntityType.Ref_Department").
Include("Ref_EntityType.Ref_Department.Ref_Locale").ToList<Entities>();
And now I'm getting all the references.
I'm still not sure why it would load all the references in the constructor but not in the RefreshEntities() method, even if the calls are identical, but this solves the problem so I'm happy enough to leave it like that.

Categories

Resources