EF Object entity State moves to Unchanged when attached to new Context - linq

I have go the oddest problem, I get an object from EF and pass it back to the Business Logic for manipulation, when finished i'm trying to save the object back to the DB, when the object is passed into the following method it's EntityState is Modified but as soon as the attach line of code is run it is set to UnChanged hence the Save will not work.
Does anyone know why EF would do this?
public void Save(IEntity entity)
{
using (var context = new eDocumentEntities())
{
using (var scope = new TransactionScope())
{
if (entity.Id != 0)
context.AttachTo(entity.EntitySet, entity);
else
context.AddObject(entity.EntitySet, entity);
context.SaveChanges();
scope.Complete();
}
}
}

Since entity change tracking is encapsulated in Context, naturally the entity loses state and other tracking stuff when it gets detached from context.

Ok I found the solution to the problem here but Ii'm more interested in an explanation, why would EF do this, seems very counter productive!?
http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx

Related

NHibernate IPreUpdateEventListener doesn't insert to second table

I'm prototyping some simple audit logging functionality. I have a mid sized entity model (~50 entities) and I'd like to implement audit logging on about 5 or 6. Ultimately I'd like to get this working on Inserts & Deletes as well, but for now I'm just focusing on the updates.
The problem is, when I do session.Save (or SaveOrUpdate) to my auditLog table from within the EventListener, the original object is persisted (updated) correctly, but my AuditLog object never gets inserted.
I think it's a problem with both the Pre and Post event listeners being called to late in the NHibernate save life cycle for the session to still be used.
//in my ISessionFactory Build method
nHibernateConfiguration.EventListeners.PreUpdateEventListeners =
new IPreUpdateEventListener[]{new AuditLogListener()};
//in my AuditLogListener
public class AuditLogListener : IPreUpdateEventListener
{
public bool OnPreUpdate(PreUpdateEvent #event)
{
string message = //code to look at #event.Entity & build message - this works
if (!string.IsNullOrEmpty(message))
AuditLogHelper.Log(message, #event.Session); //Session is an IEventSource
return false; //Don't veto the change
}
}
//In my helper
public static void Log(string message, IEventSource session)
{
var user = session.QueryOver<User>()
.Where(x => x.Name == "John")
.SingleOrDefault();
//have confirmed a valid user is found
var logItem = new AdministrationAuditLog
{
LogDate = DateTime.Now,
Message = message,
User = user
};
(session as ISession).SaveOrUpdate(logItem);
}
When it hits the session.SaveOrUpdate() in the last method, no errors occur. No exceptions are thrown. it seems to succeed and moves on. But nothing happens. The audit log entry never appears in the database.
The only way I've been able to get this to work it to create a completely new Session & Transaction inside this method, but this isn't really ideal, as the code proceeds back out of the listener method, hits the session.Transaction.Commit() in my main app, and if that transaction fails, then I've got an orphaned log message in my audit table for somethign that never happened.
Any pointers where I might be going wrong ?
EDIT
I've also tried to SaveOrUpdate the LogItem using a child session from the events based on some comments in this thread. http://ayende.com/blog/3987/nhibernate-ipreupdateeventlistener-ipreinserteventlistener
var childSession = session.GetSession(EntityMode.Poco);
var logItem = new AdministrationAuditLog
{
LogDate = DateTime.Now,
Message = message,
User = databaseLogin.User
};
childSession.SaveOrUpdate(logItem);
Still nothing appears in my Log table in the db. No errors or exceptions.
You need to create a child session, currentSession.GetSession(EntityMode.Poco), in your OnPreUpdate method and use this in your log method. Depending on your flushmode setting, you might need to flush the child session as well.
Also, any particular reason you want to roll out your own solution? FYI, NHibernate Envers is now a pretty mature library.

Getting DataContext error while saving form

I get this error when opening one specific form. The rest is working fine and I have no clue why this one isn't.
Error: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
I get the error at _oDBConnection when I try to save. When I watch _oDBConnection while running through the code, it does not exist. Even when I open the main-window it does not exist. So this form is where the DataContext is built for the very first time.
Every class inherits from clsBase where the DataContext is built.
My collegue is the professional one who built it all. I am just expanding and using it (learned it by doing it). But now I'm stuck and he is on holiday. So keep it simple :-)
What can it be?
clsPermanency
namespace Reservation
{
class clsPermanency : clsBase
{
private tblPermanency _oPermanency;
public tblPermanency PermanencyData
{
get { return _oPermanency; }
set { _oPermanency = value; }
}
public clsPermanency()
: base()
{
_oPermanency = new tblPermanency();
}
public clsPermanency(int iID)
: this()
{
_oPermanency = (from oPermanencyData in _oDBConnection.tblPermanencies
where oPermanencyData.ID == iID
select oPermanencyData).First();
if (_oPermanency == null)
throw new Exception("Permanentie niet gevonden");
}
public void save()
{
if (_oPermanency.ID == 0)
{
_oDBConnection.tblPermanencies.InsertOnSubmit(_oPermanency);
}
_oDBConnection.SubmitChanges();
}
}
}
clsBase
public class clsBase
{
protected DBReservationDataContext _oDBConnection;
protected int _iID;
public int ID
{
get { return _iID; }
}
public DBReservationDataContext DBConnection
{
get { return _oDBConnection; }
}
public clsBase()
{
_oDBConnection = new DBReservationDataContext();
}
}
Not a direct answer, but this is really bad design, sorry.
Issues:
One context instance per class instance. Pretty incredible. How are you going to manage units of work and transactions? And what about memory consumption and performance?
Indirection: every entity instance (prefixed o) is wrapped in a cls class. What a hassle to make classes cooperate, if necessary, or to access their properties.
DRY: far from it. Does each clsBase derivative have the same methods as clsPermanency?
Constructors: you always have to call the base constructor. The constructor with int iID always causes a redundant new object to be created, which will certainly be a noticeable performance hit when dealing with larger numbers. A minor change in constructor logic may cause the sequence of constructor invocations to change. (Nested and inherited constructors are always tricky).
Exception handling: you need a try-catch everywhere where classes are created. (BTW: First() will throw its own exception if the record is not there).
Finally, not a real issue, but class and variable name prefixes are sooo 19xx.
What to do?
I don't think you can change your colleague's design in his absence. But I'd really talk to him about it in due time. Just study some linq-to-sql examples out there to pick up some regular patterns.
The exception indicates that somewhere between fetching the _oPermanency instance (in the Id-d constructor) and saving it a new _oDBConnection is created. The code as shown does not reveal how this could happen, but I assume there is more code than this. When you debug and check GetHashCode() of _oDBConnection instances you should be able to find where it happens.

In LINQ-SQL, wrap the DataContext is an using statement - pros cons

Can someone pitch in their opinion about pros/cons between wrapping the DataContext in an using statement or not in LINQ-SQL in terms of factors as performance, memory usage, ease of coding, right thing to do etc.
Update: In one particular application, I experienced that, without wrapping the DataContext in using block, the amount of memory usage kept on increasing as the live objects were not released for GC. As in, in below example, if I hold the reference to List of q object and access entities of q, I create an object graph that is not released for GC.
DataContext with using
using (DBDataContext db = new DBDataContext())
{
var q =
from x in db.Tables
where x.Id == someId
select x;
return q.toList();
}
DataContext without using and kept alive
DBDataContext db = new DBDataContext()
var q =
from x in db.Tables
where x.Id == someId
select x;
return q.toList();
Thanks.
A DataContext can be expensive to create, relative to other things. However if you're done with it and want connections closed ASAP, this will do that, releasing any cached results from the context as well. Remember you're creating it no matter what, in this case you're just letting the garbage collector know there's more free stuff to get rid of.
DataContext is made to be a short use object, use it, get the unit of work done, get out...that's precisely what you're doing with a using.
So the advantages:
Quicker closed connections
Free memory from the dispose (Cached objects in the content)
Downside - more code? But that shouldn't be a deterrent, you're using using properly here.
Look here at the Microsoft answer: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2625b105-2cff-45ad-ba29-abdd763f74fe
Short version of if you need to use using/.Dispose():
The short answer; no, you don't have to, but you should...
Well, It's an IDisposable, so I guess it's not a bad idea. The folks at MSFT have said that they made DataContexts as lightweight as possible so that you may create them with reckless abandon, so you're probably not gaining much though.....
First time DataContext will get object from DB.
Next time you fire a query to get the same object (same parameters).: You’ll see query in a profiler but your object in DataContext will not be replaced with new one from DB !!
Not to mention that behind every DataContext is identity map of all objects you are asking from DB (you don’t want to keep this around).
Entire idea of DataContext is Unit Of Work with Optimistic Concurrency.
Use it for short transaction (one submit only) and dispose.
Best way to not forget dispose is using ().
I depends on the complexity of your Data Layer. If every call is a simple single query, then each call can be wrapped in the Using like in your question and that would be fine.
If, on the other hand, your Data Layer can expect multiple sequential calls from the Business Layer, the you'd wind up repeatedly creating/disposing the DataContext for each larger sequence of calls. not ideal.
What I've done is to create my Data Layer object as IDisposible. When it's created, the DataContext is created (or really, once the first call to a method is made), and when the Data Layer object disposes, it closes and disposes the DataContext.
here's what it looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace PersonnelDL
{
public class PersonnelData : IDisposable
{
#region DataContext management
/// <summary>
/// Create common datacontext for all data routines to the DB
/// </summary>
private PersonnelDBDataContext _data = null;
private PersonnelDBDataContext Data
{
get
{
if (_data == null)
{
_data = new PersonnelDBDataContext(ConfigurationManager.ConnectionStrings["PersonnelDB"].ToString());
_data.DeferredLoadingEnabled = false; // no lazy loading
//var dlo = new DataLoadOptions(); // dataload options go here
}
return _data;
}
}
/// <summary>
/// close out data context
/// </summary>
public void Dispose()
{
if (_data != null)
_data.Dispose();
}
#endregion
#region DL methods
public Person GetPersonByID(string userid)
{
return Data.Persons.FirstOrDefault(p => p.UserID.ToUpper().Equals(userid.ToUpper()));
}
public List<Person> GetPersonsByIDlist(List<string> useridlist)
{
var ulist = useridlist.Select(u => u.ToUpper().Trim()).ToList();
return Data.Persons.Where(p => ulist.Contains(p.UserID.ToUpper())).ToList();
}
// more methods...
#endregion
}
}
In one particular application, I experienced that, without wrapping the DataContext in using block, the amount of memory usage kept on increasing as the live objects were not released for GC. As in, in below example, if I hold the reference to List<Table> object and access entities of q, I create an object graph that is not released for GC.
DBDataContext db = new DBDataContext()
var qs =
from x in db.Tables
where x.Id == someId
select x;
return qs.toList();
foreach(q in qs)
{
process(q);
// cannot dispose datacontext here as the 2nd iteration
// will throw datacontext already disposed exception
// while accessing the entity of q in process() function
//db.Dispose();
}
process(Table q)
{
// access entity of q which uses deferred execution
// if datacontext is already disposed, then datacontext
// already disposed exception is thrown
}
Given this example, I cannot dispose the datacontext because all the Table instances in list variable qs **share the same datacontext. After Dispose(), accessing the entity in process(Table q) throws a datacontext already disposed exception.
The ugly kluge, for me, was to remove all the entity references for q objects after the foreach loop. The better way is to of course use the using statement.
As far as my experience goes, I would say use the using statement.

Why is FxCop raising the error "Types that own disposable fields should be disposable" on a class with no disposable fields?

I have a LINQ object with an additional method added to it. The class has no disposable properties or methods, but FxCop is raising the error "Types that own disposable fields should be disposable" and referencing that class.
I've reduced the code this far and still receive the error:
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
However, if I remove EITHER of the from clauses, FxCop stops giving the error:
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
Or
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
select new {};
return null;
}
}
}
PagePermissionSet is not disposable.
Is this a false positive? Or is the LINQ code somehow generating a disposable field on the class? If it isn't a false positive, FxCop is recommending that I implement the IDisposable interface, but what would I do in the Dispose method?
EDIT:
The full FxCop error is:
"Implement IDisposable on 'WikiPage' because it
creates members of the following IDisposable types:
'WikiTomeDataContext'. If 'WikiPage' has previously
shipped, adding new members that implement IDisposable
to this type is considered a breaking change to existing
consumers."
Edit 2:
This is the disassembled code that raises the error:
public PagePermissionSet GetUserPermissions(Guid? userId)
{
using (WikiTomeDataContext context = new WikiTomeDataContext())
{
ParameterExpression CS$0$0001;
ParameterExpression CS$0$0003;
var permissions = context.Wikis.SelectMany(Expression.Lambda<Func<Wiki, IEnumerable<VirtualWikiPageTag>>>(Expression.Property(Expression.Constant(context), (MethodInfo) methodof(WikiTomeDataContext.get_VirtualWikiPageTags)), new ParameterExpression[] { CS$0$0001 = Expression.Parameter(typeof(Wiki), "wiki") }), Expression.Lambda(Expression.New((ConstructorInfo) methodof(<>f__AnonymousType8..ctor), new Expression[0], new MethodInfo[0]), new ParameterExpression[] { CS$0$0001 = Expression.Parameter(typeof(Wiki), "wiki"), CS$0$0003 = Expression.Parameter(typeof(VirtualWikiPageTag), "pageTag") }));
return null;
}
}
Edit 3:
There does appear to be a closure class containing a reference to the DataContext. Here is its disassembled code:
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public WikiTomeDataContext context;
// Methods
public <>c__DisplayClass1();
}
My guess is that the two From clauses generate a call to SelectMany with a closure on your data context. The instance of the closure has a field to the datacontext which is causes the FxCop warning. This is nothing to worry about.
There's only one instance of your datacontext, which you clean up via the using block. Because the closure doesn't have a finalizer there's no performance or saftey implication here in the FxCop warning.
I noticed that this is a partial class. Have you checked the other implementation file for the class and see if it has an IDisposable member that is not being disposed?
I don't think the generated closure is at fault here. Closures are generated with certain attributes that should cause FxCop to ignore warnings like this.
EDIT
Further investigation by the OP showed this to be an issue with an IDisposable field being lifted into a closure.
Unfortunately there isn't a whole lot you can do about this. There is no way to make the closure implement IDisposable. Event if you could there is no way to call IDisposable on the closure instance.
The best way to approach this problem is to rewrite your code in such a way that a disposable value does not get captured in the closure. Disposable fields should always be disposed when they are finished and capturing it in a closure prevents you from doing this.
If you're returning a LINQ query from your method, consumers will iterate over the results using foreach.
When a consumer finishes a foreach loop, it internally calls dispose on the IEnumerable source (in this case, your LINQ query). This will dispose the WikiTomeDataContext.
However, if a consumer made a call to method returning a LINQ query but never iterated over the results, it would appear that enumerable would never be disposed (that is, until the garbage collector cleaned up the object). This would lead to your WikiTomeDataContext not being disposed until garbage collection.
One way you might be able to get around this problem is by calling .ToArray on the result of your LINQ query, call dispose on your context, then return the array.
Your code that gives the error uses WikiDataContext.
Your two examples that do not give an error use WikiTomeDataContext.
Maybe there is some difference between these two that is causing the error.

How can I reject all changes in a Linq to SQL's DataContext?

On Linq to SQL's DataContext I am able to call SubmitChanges() to submit all changes.
What I want is to somehow reject all changes in the datacontext and rollback all changes (preferable without going to the database).
Is this possible?
Why not discard the data context and simply replace it with a new instance?
public static class DataContextExtensions
{
/// <summary>
/// Discard all pending changes of current DataContext.
/// All un-submitted changes, including insert/delete/modify will lost.
/// </summary>
/// <param name="context"></param>
public static void DiscardPendingChanges(this DataContext context)
{
context.RefreshPendingChanges(RefreshMode.OverwriteCurrentValues);
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
//Undo inserts
foreach (object objToInsert in changeSet.Inserts)
{
context.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in changeSet.Deletes)
{
context.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
}
/// <summary>
/// Refreshes all pending Delete/Update entity objects of current DataContext according to the specified mode.
/// Nothing will do on Pending Insert entity objects.
/// </summary>
/// <param name="context"></param>
/// <param name="refreshMode">A value that specifies how optimistic concurrency conflicts are handled.</param>
public static void RefreshPendingChanges(this DataContext context, RefreshMode refreshMode)
{
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
context.Refresh(refreshMode, changeSet.Deletes);
context.Refresh(refreshMode, changeSet.Updates);
}
}
}
Refer to Linq to SQL - Discard Pending Changes
In .net 3.0 use the db.GetChangeSet().Updates.Clear() for updated, db.GetChangeSet().Inserts.Clear() for new or db.GetChangeSet().Deletes.Clear() for deleted items.
In .net 3.5 and above the result of GetChangeSet() is now readonly, loop the collection in for or foreach and refresh every ChangeSet table like also macias wrote in his comment.
As Haacked said, just drop the data context.
You probably shouldn't keep the data context alive for a long time. They're designed to be used in a transactional manner (i.e. one data context per atomic work unit). If you keep a data context alive for a long time, you run a greater risk of generating a concurrency exception when you update a stale entity.
Calling Clear() on the Updates, Deletes and Inserts collection does not work.
GetOriginalEntityState() can be useful, but it only gives the IDs for foreign key relationships, not the actual entities so you're left with a detached object.
Here's an article that explains how to discard changes from the data context: http://graemehill.ca/discard-changes-in-linq-to-sql-datacontext
EDIT: Calling Refresh() will undo updates, but not deletes and inserts.
The Refresh will work, however you have to give the entities you want to reset.
For example
dataContext.Refresh(RefreshMode.OverwriteCurrentValues, someObject);
You can use the GetOriginalEntityState(..) to get the original values for the objects e.g. Customers using the old cached values.
You can also iterate through the changes e.g. updates and refresh the specific objects only and not the entire tables because the performance penalty will be high.
foreach (Customer c in MyDBContext.GetChangeSet().Updates)
{
MyDBContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, c);
}
this will revert the changes using persisted data in the database.
Another solution is to dump the datacontext you use, using Dispose().
In any case it is a good practice to override the Insert and Remove methods in the collection of e.g. Customers you use and add e.g. an InsertOnSubmit() call. This will resolve your issue with pending Insertions and Deletions.
My application is outlook style with a icon to select an active form (ListBox). Before allowing the user to change their context they have to accept changes or discard them.
var changes = db.GetChangeSet();
if ((changes.Updates.Count > 0) || (changes.Inserts.Count > 0) || (changes.Deletes.Count > 0))
{
if (MessageBox.Show("Would you like to save changes?", "Save Changes", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
db.SubmitChanges();
} else
{
//Rollback Changes
foreach (object objToInsert in changes.Inserts)
{
db.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
foreach (object objToDelete in changes.Deletes)
{
db.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
foreach (object objToUpdate in changes.Updates)
{
db.Refresh(RefreshMode.OverwriteCurrentValues, objToUpdate);
}
CurrentForm.SetObject(null); //Application Code to Clear active form
RefreshList(); //Application Code to Refresh active list
}
}
Excellent write up on here, but here is a copy and paste of the code used.
Public Sub DiscardInsertsAndDeletes(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Delete the insertions
For Each insertion In changes.Inserts
data.GetTable(insertion.GetType).DeleteOnSubmit(insertion)
Next
' Insert the deletions
For Each deletion In changes.Deletes
data.GetTable(deletion.GetType).InsertOnSubmit(deletion)
Next
End Sub
Public Sub DiscardUpdates(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Refresh the tables with updates
Dim updatedTables As New List(Of ITable)
For Each update In changes.Updates
Dim tbl = data.GetTable(update.GetType)
' Make sure not to refresh the same table twice
If updatedTables.Contains(tbl) Then
Continue For
Else
updatedTables.Add(tbl)
data.Refresh(RefreshMode.OverwriteCurrentValues, tbl)
End If
Next
End Sub
Here is how I did it. I just followed Teddy's example above and simplified it. I have one question though, why even bother with the refresh on the DELETES?
public static bool UndoPendingChanges(this NtsSuiteDataContext dbContext)
{
if (dbContext.ChangesPending())
{
ChangeSet dbChangeSet = dbContext.GetChangeSet();
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Deletes);
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Updates);
//Undo Inserts
foreach (object objToInsert in dbChangeSet.Inserts)
{
dbContext.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in dbChangeSet.Deletes)
{
dbContext.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
return true;
}
//This works for me, 13 years later using Dotnet (Core) 6:
dbContext.ChangeTracker.Clear();

Resources