RavenDB is not tracking entity changes - c#

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.

Related

data context save changes not working in C# entity framwork

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

Erasing AutoCAD drawing objects after exploding them, using C#

I am working on a Task in which the code automatically opens a drawing selected by the user [in a UI] and selects all the objects in the drawing and starts to explode all the of them till they cant be exploded anymore. While doing this I face a problem, the original (un-exploded 3D object) is still present in the drawing, super imposed by the Exploded object. Every recursive call of the Explode function creates a new exploded 3D object of that object.
Here is a snippet of the code I working on:
PromptSelectionResult ss = ed.SelectAll();
using (DocumentLock acLckDoc = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
objs = new DBObjectCollection();
foreach (SelectedObject so in ss.Value)
{
Entity ent = (Entity)tr.GetObject(so.ObjectId, OpenMode.ForWrite);
if (!(ent is Solid3d))
{
ent.Explode(objs);
ent.UpgradeOpen();
ent.Erase();
}
}
tr.Commit();
}
}
As soon as the control comes on to the ent.Erase() statement - it throws an exception, eCannotBeErasedByCaller. I cant figure out why? I have unlocked all layers, opened the entity for Write, CommandFlags have been set to Session and UsePickSet (shuffled through all).
Anybody got any suggestions?
Looking at your description, you probably need a recursive explode. Sometime ago I did a code around this, for other type of entities, but you can adjust it.
private List<DBObject> FullExplode(Entity ent)
{
// final result
List<DBObject> fullList = new List<DBObject>();
// explode the entity
DBObjectCollection explodedObjects = new DBObjectCollection();
ent.Explode(explodedObjects);
foreach (Entity explodedObj in explodedObjects)
{
// if the exploded entity is a blockref or mtext
// then explode again
if (explodedObj.GetType() == typeof(BlockReference) ||
explodedObj.GetType() == typeof(MText))
{
fullList.AddRange(FullExplode(explodedObj));
}
else
fullList.Add(explodedObj);
}
return fullList;
}
source: http://adndevblog.typepad.com/infrastructure/2013/04/get-cogopoint-label-text.html
I finally found out the reason why the Original objects werent getting erased.
In the earlier part of the code, a AutoCAD Plant3D dwg is exported to AutoCAD (ExporttoAutoCAD / Saveas), this was creating Proxy items. These cant be deleted manually or via code.
Only way is to explode the PipeLines and Inline assets before exporting the file. This happens automatically if you export the file, but if you use saveas, you will have to explode the Pipe components before you export the file.
Wasted a lot of time understanding the cause, but finally got it!

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.

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.

Implementing Audit Log / Change History with MVC & Entity Framework

I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm), we find the object we are trying to update, and then use TryUpdateModel(object) to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object) has done its work. i.e.
[HttpPost]
public ActionResult Edit(ViewModel vm)
{
//Need to take the copy here
var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);
if (ModelState.IsValid)
{
//Form the un edited view model
var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
//Compare with old view model
WriteChanges(uneditedVM, vm);
...
TryUpdateModel(object);
}
...
}
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object); throws an UpdateException.
So the question is - in this situation - how do I create a copy of the object outside of EntityFramework to compare for change/audit history, so that it does not affect or change the
EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges() but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities
{
public override int SaveChanges(SaveOptions options)
{
DetectChanges();
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
//return base.SaveChanges();
return base.SaveChanges(options);
}
}
IF you are using EF 4 you can subscribe to the SavingChanges event.
Since Entities is a partial class you can add additional functionality in a separate file. So create a new file named Entities and there implement the partial method OnContextCreated to hook up the event
public partial class Entities
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
}
If you are using EF 4.1 you can go through this article to extract changes
See FrameLog, an Entity Framework logging library that I wrote for this purpose. It is open-source, including for commercial use.
I know that you would rather just see a code snippet showing how to do this, but to properly handle all the cases for logging, including relationship changes and many-to-many changes, the code gets quite large. Hopefully the library will be a good fit for your needs, but if not you can freely adapt the code.
FrameLog can log changes to all scalar and navigation properties, and also allows you to specify a subset that you are interested in logging.
There is an article with high rating here at the codeproject: Implementing Audit Trail using Entity Framework . It seems to do what you want. I have started to use this solution in a project. I first wrote triggers in T-SQL in the database but it was too hard to maintain them with changes in the object model happening all the time.

Categories

Resources