Updating LINQ to SQL object causing System.NotSupportedException - linq

I get System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new perhaps having been loaded from another DataContext when I want to update an object's with child entities.
The scenario is like this:
I have a SubscriberProvider that allows me to create subscribers.
var provider = new SubscriberProvider(); // Creates a new repository with own datacontext
var newSubscriber = new Subscriber
{
EmailAddress = emailAddress,
};
newSubscriber.Interests.Add(new Interest{
Id=1,
Name="cars"
});
provider.Subscribe(newSubscriber);
On a normal subscribe page, this works fine.
Now I have a linq2sql Member class(retrievable by a MemberRepository) and I want to extend it to have a helper subscribe method like so:
var repository = new MembershipRepository(); // Holds its own datacontext
var member = repository.Get("member1");
member.Subscribe(); // transfer member's info and interests to subscriber's table
The exception occurs when SubscriberProvider tries to add interests of the member.
Commenting out
newSubscriber.Interests.Add(new Interest{
Id=1,
Name="cars"
});
will make member.Subscribe() work.
member.Subscribe() is simply:
public void Subscribe(bool emailIsVerified, bool receiveEmails, bool sendDoubleOptIn)
{
var provider = new MailingListProvider();
provider.Subscribe(EmailAddress, emailIsVerified, receiveEmails, CountryId, sendDoubleOptIn, ConvertInterests(MemberInterests.ToList()));
}
So what's causing the child entities(Interests) to lose their datacontext when I do member.Subscribe() and how do I go about fixing this?

It seems there's some code missing here, but I'll take a stab anyway because I think I have an idea what's going on.
If you have a different DataContext created for your MembershipRepository and your SubscriberRepository you're going to have issues related to entities "having been loaded from another DataContext." (as the Exception you posted points out). You can't just take an object out of one DataContext and save it into another.
It seems that you might have an architectural issue here. Should these 2 repositories actually be separate? If so, should they have completely different DataContexts? I would probably recommend using Dependency Injection to inject your DataContexts into your Repositories. Then you can decide how to cache your DataContexts.

That line of code you commented out is being flagged by the DataContext as a new record, even though it's likely that the record already exists, due to the error message.
Change the line to:
newSubscriber.Interests.Add(DataContext.Interests.Where(a => a.Id == 1).Single());
Now, the DataContext will know that record is one that already exists, and won't try to add it as an Insert to the ChangeSet.

Found the solution to this myself. Turns out it was the ConvertInterests() method causing it. The converted interest object had an invalid declaration which compiled ok.
Thinking the code was simple enough, I didn't create a test for it. I should have known better!

Related

Get notified when TaggedValue of an element changes in Enterprise Architect

I am new in creating Addins for Enterprise Architect and I have this problem:
I have a diagram with elements which have TaggedValues. I want to get notified when the value of a TaggedValue changes and see the new value.
I saw that there is this event EA_OnElementTagEdit available but I can't seem to get it triggered. I also saw that the tagged value has to be of type AddinBroadcast but I can't seem to make it work. What am I missing?
I will put below a sample of my code:
//creating tagged value
EA.TaggedValue ob3 = (EA.TaggedValue)NewElement.TaggedValues.AddNew("Responsible", "val");
ob3.Value = EEPROMBlocks.ElementAt(index).Responsible;
ob3.SetAttribute("Type", "AddinBroadcast");
ob3.Update();
//event method
public override void EA_OnElementTagEdit(EA.Repository Repository, long ObjectID, ref string TagName, ref string TagValue, ref string TagNotes)
You are not missing anything. This is simply not possible. The only way around is the OnContext... where you temporarily store the status of one element and look if a tag has changed when the context changes. I would not recommend that since it involved a lot of superfluous DB accesses.
Send a feature request (if you are an optimistic guy). Alternatively you should think of ways to get around this somehow else.

How do I do cross-entity server-side validation

In my application, I have cross-entity validation logic that requires me to look at the entire change set and I'm doing this using the BeforeSaveEntities override.
I can construct the right logic by examining the saveMap parameter, but what am I supposed to do if I find something invalid?
If I throw an exception, like I would for single entity validation in the BeforeSaveEntity override, the whole save is aborted and the error is reported to the client. But some of the entities might be valid so I would want to save those and only abort the invalid parts.
Because BeforeSaveEntities returns a saveMap, I think I should be able to remove the invalid entities from the change set and continue to save the valid entities, but then how do I report the invalid parts to the client?
Is it possible to do a partial save of only the valid entities and at the same time, report a sensible error to the client to describe the parts of the save that failed?
Jay told you the way it is.
I wouldn't hold my breath waiting for Breeze to change because I think yours is a rare scenario and it isn't one we would want to encourage anyway.
But I'm weird and I can't stop thinking what I'd do if were you and I absolutely HAD to do it. I might try something like this.
Warning: this is pseudo-code and I'm making this up. I do not recommend or warrant this
Create a custom MyCustomEFContextProvider that derives from EFContextProvider.
Give it an ErrorEntities property to hold the error object
Override (shadow) the SaveChanges method with another that delegates to the base
public new CustomSaveResult SaveChanges(JObject saveBundle,
TransactionSettings transactionSettings = null) {
var result = base.SaveChanges(saveBundle, transactionSettings);
// learn about CustomSaveResult below
return new CustomSaveResult(this.ErrorEntities, result);
}
Catch an invalid entity inside BeforeSaveEntities
Pass it with error message to your custom ErrorEntities property
You get to that property via the EntityInfo instance as in
((MyCustomEFContextProvider) info.ContextProvider).ErrorEntities.Add(new ErrorEntity(info, message));
Remove the invalid entity from the SaveMap so it won't be included in the actual save
Let the save continue
The second line of your override SaveChanges method creates a new instance of your CustomSaveResult from the standard one and returns that to the caller.
public class CustomSaveResult : SaveResult {
public List ErrorEntities;
public CustomSaveResult(List errorEntities, SaveResult result){
// copy over everything
this.Entities = result.Entities;
this.KeyMappings = result.KeyMappings;
this.Errors = this.Errors;
// and now your error stuff
this.ErrorEntities = errorEntities;
}
}
Let's assume the caller is your Web API controller's SaveChanges method. Well you don't have to change a thing but you might make it clear by explicitly returning your custom SaveResult:
readonly MyCustomEFContextProvider _contextProvider = new MyCustomEFContextProvider();
...
[HttpPost]
public CustomSaveResult SaveChanges(JObject saveBundle) {
return _contextProvider.SaveChanges(saveBundle);
}
JSON.Net will happily serialize the usual material + your custom ErrorEntities property (be sure to make it serializable!) and send it to the Breeze client.
On the Breeze client you write your own variation on the stock Breeze Web API data service adapter. Yours does almost exactly the same thing as the Breeze version. But, when processing the save payload from the server, it also extracts this extra "error entities" material in the response and does whatever you want to do with it.
I don't know what that will be but now you have it.
See how easy that was? LOL.
Breeze does not currently support a save mechanism that both saves and returns an error at the same time. While possible this seems a bit baroque.
As you pointed out, you can
1) Throw an exception inside of the BeforeSaveEntities and fail the save. You can even specify which specific entity or entities caused the failure and why. In this case the entire save is aborted.
or
2) Remove 'bad' items from the saveMap within the BeforeSaveEntities and save only a subset of what was passed in. In this case you are performing a partial save.
But we don't support a hybrid of these two. Please add this to the Breeze User Voice if you feel strongly and we can see if other members of the community feel that this would be useful.

How to revert a specific property to its saved value (EF4)?

Well, I'm using MVC3 and EF4 and everything is great... Except when i have to update an object but not all of its properties.
Here's how its implemented so far:
I have a class, it's properties, and a DbSet for it on my DbContext. My page is sending back a JSon with all values of that object (including the ones I want to update and the ones i want to be left unchanged). On the controller, i'm receiving the object and just changing it's State to EntityState.Modified and calling the SaveChanges() of the DbContext.
As I said, everything works great, but i do want that some of the values received by that JSon to be discarded, leaving their values untouched as they are on the database.
How can I achieve that?
Thanks in advance for any help...
Not sure of your code but when you only want to update certain properties on an update call to EF you should do it like so....
db.Person.Attach(person);
db.Entry(person).Property(x => x.Name).IsModified = true;
db.SaveChanges();
so you will modify whichever properties you want and leave alone which ever ones you don't.
You may also try this to..
using (DBContext newCtx = new DBContext())
{
newCtx.Employee.Attach(emp);
newCtx.ObjectStateManager.ChangeObjectState(emp, System.Data.EntityState.Modified);
newCtx.SaveChanges();
}
Reference : Update Entity in Entity Framework

Refresh LINQ db-connection object

My collegue helped me with starting programming in c# although I had no experience but I like it. All went well until I came across some problems we both can't fix. He uses SQL himself but started me up with LINQ.
To do a LINQ-query I use this object : _oDBConnection (in clsApplication.cs)
So when opening the programm this object is built. But it creates some problems:
When saving a new object (putting data into table), I cannot load those values with a query. I need to restart the programm.
When running 2 instances of the programm, one is not getting the latest values when changed in the other (but it is showing the new ones but not the changed ones!)
According to these problems I can only conclude that when I call clsApplication._oDBConnection.tblTAble a second time it is not relinking again to the db but is giving me the old db-states back.
This is the code he built:
public static DBReservationDataContext _oDBConnection;
private static frmMain _fMain;
public clsApplication()
{
Thread.CurrentThread.Name = "main";
clsErrorLog.ErrorLocation = "C:\\Software\\ErrorLog";
clsErrorLog.setPassword("*****");
clsErrorLog.LockApplication += new clsErrorLog.dLockApplication(lockApplication);
_oDBConnection = new DBReservationDataContext();
_fMain = new frmMain();
_fMain.Show();
}
What can I do to fix this problem?
Example:
although present in the database, it crashes on this query because the entity with id == iID is not found. But the iID is correct and it does exist in the database. The query will work after closing and restarting the programm. Then the clsApplication is called again.
public clsReservationDetail(int iID)
:this()
{
_oReservationDetail = (from oReservationDetailQuery in clsApplication._oDBConnection.tblReservationDetails
where oReservationDetailQuery.ID == iID
select oReservationDetailQuery).First();
}
thx in advance
Your data context will have a Refresh method which will clear any cached results, and should allow your query to complete with no problems
The static keyword makes it so that you have one reference per AppDomain. That is the wrong way to use DataContext instances.
Each instance of DataContext tracks the objects it has seen. This is for consistency. If you get a Customer instance with CustomerID = 4 from one query, you should get the same Customer instance from another query that returns the CustomerID = 4 record.
If you want to see the changes in the database, you must
1) Tell the datacontext to stop tracking changes. This must be done before the first query and makes the datacontext instance unable to SubmitChanges (since it can't track them anymore).
OR
2) Tell the datacontext to Refresh each instance you suspect has changed. If you do that, you should specify how resolve the conflict between your local changes and the remote changes - the simplest way of resolving this conflict is to have no local changes.
OR
3) (The right way) Make a new DataContext instance and load the record with that!
Also note: Since DataContext implements IDisposable, you are required to call Dispose when you are done with each instance even when exceptions occur. The using block is a good way to get that to happen.

Debugging LINQ to SQL SubmitChanges()

I am having a really hard time attempting to debug LINQ to SQL and submitting changes.
I have been using http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx, which works great for debugging simple queries.
I'm working in the DataContext Class for my project with the following snippet from my application:
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.SubmitChanges();
I will catch some very odd exceptions when I run this.SubmitChanges;
Index was outside the bounds of the array.
The stack trace goes places I cannot step into:
at System.Data.Linq.IdentityManager.StandardIdentityManager.MultiKeyManager`3.TryCreateKeyFromValues(Object[] values, MultiKey`2& k)
at System.Data.Linq.IdentityManager.StandardIdentityManager.IdentityCache`2.Find(Object[] keyValues)
at System.Data.Linq.IdentityManager.StandardIdentityManager.Find(MetaType type, Object[] keyValues)
at System.Data.Linq.CommonDataServices.GetCachedObject(MetaType type, Object[] keyValues)
at System.Data.Linq.ChangeProcessor.GetOtherItem(MetaAssociation assoc, Object instance)
at System.Data.Linq.ChangeProcessor.BuildEdgeMaps()
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges()
at JobTrakDataContext.CreateNewJob(NewJob job, String userName) in D:\JobTrakDataContext.cs:line 1119
Does anyone have any tools or techniques they use? Am I missing something simple?
EDIT:
I've setup .net debugging using Slace's suggestion, however the .net 3.5 code is not yet available: http://referencesource.microsoft.com/netframework.aspx
EDIT2:
I've changed to InsertOnSubmit as per sirrocco's suggestion, still getting the same error.
EDIT3:
I've implemented Sam's suggestions trying to log the SQL generated and to catch the ChangeExceptoinException. These suggestions do not shed any more light, I'm never actually getting to generate SQL when my exception is being thrown.
EDIT4:
I found an answer that works for me below. Its just a theory but it has fixed my current issue.
I always found useful to know exactly what changes are being sent to the DataContext in the SubmitChanges() method.
I use the DataContext.GetChangeSet() method, it returns a ChangeSet object instance that holds 3 read-only IList's of objects which have either been added, modified, or removed.
You can place a breakpoint just before the SubmitChanges method call, and add a Watch (or Quick Watch) containing:
ctx.GetChangeSet();
Where ctx is the current instance of your DataContext, and then you'll be able to track all the changes that will be effective on the SubmitChanges call.
First, thanks everyone for the help, I finally found it.
The solution was to drop the .dbml file from the project, add a blank .dbml file and repopulate it with the tables needed for my project from the 'Server Explorer'.
I noticed a couple of things while I was doing this:
There are a few tables in the system named with two words and a space in between the words, i.e. 'Job Master'. When I was pulling that table back into the .dbml file it would create a table called 'Job_Master', it would replace the space with an underscore.
In the orginal .dbml file one of my developers had gone through the .dbml file and removed all of the underscores, thus 'Job_Master' would become 'JobMaster' in the .dbml file. In code we could then refer to the table in a more, for us, standard naming convention.
My theory is that somewhere, the translation from 'JobMaster' to 'Job Master' while was lost while doing the projection, and I kept coming up with the array out of bounds error.
It is only a theory. If someone can better explain it I would love to have a concrete answer here.
My first debugging action would be to look at the generated SQL:
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.Log = Console.Out; // prints the SQL to the debug console
this.SubmitChanges();
The second would be to capture the ChangeConflictException and have a look at the details of failure.
catch (ChangeConflictException e)
{
Console.WriteLine("Optimistic concurrency error.");
Console.WriteLine(e.Message);
Console.ReadLine();
foreach (ObjectChangeConflict occ in db.ChangeConflicts)
{
MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
Customer entityInConflict = (Customer)occ.Object;
Console.WriteLine("Table name: {0}", metatable.TableName);
Console.Write("Customer ID: ");
Console.WriteLine(entityInConflict.CustomerID);
foreach (MemberChangeConflict mcc in occ.MemberConflicts)
{
object currVal = mcc.CurrentValue;
object origVal = mcc.OriginalValue;
object databaseVal = mcc.DatabaseValue;
MemberInfo mi = mcc.Member;
Console.WriteLine("Member: {0}", mi.Name);
Console.WriteLine("current value: {0}", currVal);
Console.WriteLine("original value: {0}", origVal);
Console.WriteLine("database value: {0}", databaseVal);
}
}
}
You can create a partial class for your DataContext and use the Created or what have you partial method to setup the log to the console.out wrapped in an #if DEBUG.. this will help you to see the queries executed while debugging any instance of the datacontext you are using.
I have found this useful while debugging LINQ to SQL exceptions..
partial void OnCreated()
{
#if DEBUG
this.Log = Console.Out;
#endif
}
The error you are referring to above is usually caused by associations pointing in the wrong direction. This happens very easily when manually adding associations to the designer since the association arrows in the L2S designer point backwards when compared to data modelling tools.
It would be nice if they threw a more descriptive exception, and maybe they will in a future version. (Damien / Matt...?)
This is what I did
...
var builder = new StringBuilder();
try
{
context.Log = new StringWriter(builder);
context.MY_TABLE.InsertAllOnSubmit(someData);
context.SubmitChanges();
}
finally
{
Log.InfoFormat("Some meaningful message here... ={0}", builder);
}
A simple solution could be to run a trace on your database and inspect the queries run against it - filtered ofcourse to sort out other applications etc. accessing the database.
That ofcourse only helps once you get past the exceptions...
VS 2008 has the ability to debug though the .NET framework (http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx)
This is probably your best bet, you can see what's happening and what all the properties are at the exact point in time
Why do you do UpdateJobMaster on a new instance ? Shouldn't it be InsertOnSubmit ?
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.InsertOnSubmit(newJobToCreate);
this.SubmitChanges();
This almost certainly won't be everyone's root cause, but I encountered this exact same exception in my project - and found that the root cause was that an exception was being thrown during construction of an entity class. Oddly, the true exception is "lost" and instead manifests as an ArgumentOutOfRange exception originating at the iterator of the Linq statement that retrieves the object/s.
If you are receiving this error and you have introduced OnCreated or OnLoaded methods on your POCOs, try stepping through those methods.
Hrm.
Taking a WAG (Wild Ass Guess), it looks to me like LINQ - SQL is trying to find an object with an id that doesn't exist, based somehow on the creation of the JobMaster class. Are there foreign keys related to that table such that LINQ to SQL would attempt to fetch an instance of a class, which may not exist? You seem to be setting the ProjectID of the new object to a string - do you really have an id that's a string? If you're trying to set it to a new project, you'll need to create a new project and get its id.
Lastly, what does UpdateJobMaster do? Could it be doing something such that the above would apply?
We have actually stopped using the Linq to SQL designer for our large projects and this problem is one of the main reasons. We also change a lot of the default values for names, data types and relationships and every once in a while the designer would lose those changes. I never did find an exact reason, and I can't reliably reproduce it.
That, along with the other limitations caused us to drop the designer and design the classes by hand. After we got used to the patterns, it is actually easier than using the designer.
I posted a similar question earlier today here: Strange LINQ Exception (Index out of bounds).
It's a different use case - where this bug happens during a SubmitChanges(), mine happens during a simple query, but it is also an Index out of range error.
Cross posting in this question in case the combination of data in the questions helps a good Samaritan answer either :)
Check that all the "primary key" columns in your dbml actually relate to the primary keys on the database tables. I just had a situation where the designer decided to put an extra PK column in the dbml, which meant LINQ to SQL couldn't find both sides of a foreign key when saving.
I recently encountered the same issue: what I did was
Proce proces = unit.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
where pt.Name == "Fix-O"
select pt).Single().ProcesTypeId &&
u.UnitId == UnitId);
Instead of:
Proce proces = context.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
where pt.Name == "Fix-O"
select pt).Single().ProcesTypeId &&
u.UnitId == UnitId);
Where context was obviously the DataContext object and "unit" an instance of Unit object, a Data Class from a dbml file.
Next, I used the "proce" object to set a property in an instance of another Data Class object. Probably the LINQ engine could not check whether the property I was setting from the "proce" object, was allowed in the INSERT command that was going to have to be created by LINQ to add the other Data Class object to the database.
I had the same non speaking error.
I had a foreign key relation to a column of a table that was not the primary key of the table, but a unique column.
When I changed the unique column to be the primary key of the table the problem went away.
Hope this helps anyone!
Posted my experiences with this exception in an answer to SO# 237415
I ended up on this question when trying to debug my LINQ ChangeConflictException. In the end I realized the problem was that I manually added a property to a table in my DBML file, but I forgot to set the properties like Nullable (should have been true in my case) and Server Data Type
Hope this helps someone.
This is a long time ago, but I had the same problem and the error was because of a trigger with a select statement. Something like
CREATE TRIGGER NAME ON TABLE1 AFTER UPDATE AS SELECT table1.key from table1
inner join inserted on table1.key = inserted.key
When linq-to-sql runs the update command, it also runs a select statement to receive the auto generated values in the same query and expecting the first record set to contains the columns "asked for" but in this case the first row was the columns from the select statement in the trigger. So linq-to-sql was expecting two autogenerated columns, but it only received one column (with wrong data) and that was causing this exception.

Resources