I've got a simple relationship
I've created a simple app with the Model like the above one. Model in the application must be updated every time DB changes. I can get the latest changes by calling GetDBChanges Stored Procedure. (see method T1Elapsed)
here is the app:
class Program
{
private static int? _lastDbChangeId;
private static readonly MASR2Entities Model = new MASR2Entities();
private static readonly Timer T1 = new Timer(1000);
private static readonly Timer T2 = new Timer(1000);
private static Strategy _strategy = null;
static void Main(string[] args)
{
using (var ctx = new MASR2Entities())
{
_lastDbChangeId = ctx.GetLastDbChangeId().SingleOrDefault();
}
_strategy = Model.Strategies.FirstOrDefault(st => st.StrategyId == 224);
T1.Elapsed += T1Elapsed;
T1.Start();
T2.Elapsed += T2Elapsed;
T2.Start();
Console.ReadLine();
}
static void T2Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine("All rules: " + Model.StrategyRules.Count());
Console.WriteLine("Strategy: name=" + _strategy.Name + " RulesCount=" + _strategy.StrategyRules.Count);
}
private static void T1Elapsed(object sender, ElapsedEventArgs e)
{
T1.Stop();
try
{
using (var ctx = new MASR2Entities())
{
var changes = ctx.GetDBChanges(_lastDbChangeId).ToList();
foreach (var dbChange in changes)
{
Console.WriteLine("DbChangeId:{0} {1} {2} {3}", dbChange.DbChangeId, dbChange.Action, dbChange.TableName, dbChange.TablePK);
switch (dbChange.TableName)
{
case "Strategies":
{
var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyId=", ""));
Model.Refresh(RefreshMode.StoreWins, Model.Strategies.AsEnumerable());
}
break;
case "StrategyRules":
{
var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyRuleId=", ""));
Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable());
}
break;
}
_lastDbChangeId = dbChange.DbChangeId;
}
}
}
catch (Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
finally
{
T1.Start();
}
}
}
When I run it, this is a sample output:
All rules: 222
Strategy: name=Blabla2 RulesCount=6
then I add a row to the child table (Strategy Rule),
DbChangeId:1713 I StrategyRules StrategyRuleId=811
All rules: 223
Strategy: name=Blabla2 RulesCount=7
and finally, I remove the row from StrategyRules
DbChangeId:1714 D StrategyRules StrategyRuleId=811
All rules: 222
Strategy: name=Blabla2 RulesCount=7
Why RulesCount is still 7? How I can force EF to refresh "Navigation Property"?
What I am missing here?
---EDIT--- to cover Slauma's answer
case "StrategyRules":
{
var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyRuleId=", ""));
if (dbChange.Action == "I")
{
//Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable());
}
else if (dbChange.Action == "D")
{
var deletedRule1 = Model.StrategyRules.SingleOrDefault(sr => sr.Id == id);
//the above one is NULL as expected
var deletedRule2 = _strategy.StrategyRules.SingleOrDefault(sr => sr.Id == id);
//but this one is not NULL - very strange, because _strategy is in the same context
//_strategy = Model.Strategies.FirstOrDefault(st => st.StrategyId == 224);
}
}
ObjectContext.Refresh refreshes the scalar properties of the entities you pass into the method along with any keys that refer to related entities. If an entity you pass into the method does not exist anymore in the database because it has been deleted in the meantime Refresh does nothing with the attached entity and just ignores it. (That's a guess from my side, but I could not explain otherwise why you 1) don't get an exception on Refresh (like "cannot refresh entity because it has been deleted") and 2) the entity is apparently still attached to the context.)
Your Insert case does not work because you call Refresh but it works because you load the whole StrategyRules table into memory in this line:
Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable())
Refresh enumerates the collection in the second parameter internally. By starting the iteration it triggers the query which is just Model.StrategyRules = load the whole table. AsEnumerable() is only a switch from LINQ-to-Entities to LINQ-to-Objects, that is, every LINQ operator you would apply after AsEnumerable() is performed in memory, not on the database. Since you don't apply anything, AsEnumerable() actually has no effect on your query.
Because you load the whole table the recently inserted StrategyRule will be loaded as well and together will the key to the _strategy entity. The ObjectContext's automatic relationship fixup establishes the relationship to the navigation collection in _strategy and _strategy.StrategyRules.Count will be 7. (You could remove the Refresh call and just call Model.StrategyRules.ToList() and the result would still be 7.)
Now, all this does not work in the Delete case. You still run a query to load the whole StrategyRules table from the database but EF won't remove or detach entities from the context that are not in the result set anymore. (And as far as I know there is no option to force such an automatic removal.) The deleted entity is still in the context with its key refering to strategy and the count will remain 7.
What I am wondering is why you don't leverage that your set of DBChanges apparenty knows exactly what has been removed in the dbChange.TablePK property. Instead of using Refresh couldn't you use something like:
case "StrategyRules":
{
switch (dbChange.Action)
{
case "D":
{
var removedStrategyRule = _strategy.StrategyRules
.SingleOrDefault(sr => sr.Id == dbChange.TablePK);
if (removedStrategyRule != null)
_strategy.StrategyRules.Remove(removedStrategyRule);
}
break;
case ...
}
}
break;
Related
I would like to update one field on a parent table and add the child records in a foreach loop.
The idea is to ensure that each set of records is updated independently beacuse each parent can contain more than 100k child items and if one fails the others should still be attempted.
The parent records already exist in the database and only one field is to be updated and no child records exist yet.
The problem is that the first pass on the for each loop is actually commiting the changes for all the items on the collection instead of a single parent record and its childs. The other passes raises the duplicate primary key exception because it tries again to update and insert all items.
It is as if the foreach is being ignored and everthing is commmited at once - which is too much load and does not ensure each parent, child is an isolated transaction.
What could be causing this behaviour?
private async Task<IEnumerable<ParentTable>> SaveChildItems(IEnumerable<ParentTable> itemsToBeSaved)
{
var res = new List<ParentTable>();
foreach(var itemToSave in itemsToBeSaved)
{
var strategy = _dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async() =>
{
using (var transaction = _dbContext.Database.BeginTransaction())
{
try
{
var resFile = await _dbContext.ParentTable.Where(x => x.Name == itemsToBeSaved.Name).FirstOrDefaultAsync();
resFile.FieldToUpdate = enum.UpdateValue;
resFile.ChildTable = itemsToBeSaved.ChildTable;
await _dbContext.SaveChangesAsync();
transaction.Commit();
res.Add(itemToSave);
}
catch (System.Exception ex)
{
transaction.Rollback();
Log.Fatal(ex, LoggingTemplates.Exception, "Error on parent table file. " + ex.Message, #"\n", ex);
}
}
});
}
return res;
}
The code that works for adding each parent record along with its childs in the same app is below. I could change the logic to make work like this but there is processing that depends on the parent record and upserting is not what I am looking for.
private async Task<IEnumerable<ParentTable>> SaveRecords(IEnumerable<ParentTable> itemsToBeSaved)
{
var res = new List<ParentTable>();
foreach(var itemToSave in itemsToBeSaved)
{
var strategy = _dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async() =>
{
using (var transaction = _dbContext.Database.BeginTransaction())
{
try
{
var resFile = await _dbContext.ParentTable.AddAsync(itemToSave);
await _dbContext.SaveChangesAsync();
transaction.Commit();
res.Add(itemToSave);
}
catch (System.Exception ex)
{
transaction.Rollback();
Log.Fatal(ex, LoggingTemplates.Exception, "Error on parent table file. " + ex.Message, #"\n", ex);
}
}
});
}
return res;
}
My guess is that the "parent" tables in itemsToBeSaved being passed into the function were retrieved from the same instance of _dbContext.
In which case they are all being tracked by that context and as soon as you call
await _dbContext.SaveChangeAsync()
the first time, then EF will automatically update everything that is being tracked by that context.
Those "itemsToBeSaved" being passed in need to be un tracked.
This is what is happening:
// Retrieve 2 tables (tracked by the _dbContext))
var parents = _dbContext.ParentTable.Where(x => x.Name == "foo" || x.Name == "bar");
// Assign to two different vars
var tableFoo = parents.where(x => x.Name == "foo");
var tableBar = parents.where(x => x.Name == "bar");
// Put a child table on first one
tableFoo.ChildTable = new ChildTable() { Name = "Fum" };
// Will save ALL parent tables originally retrieved
// and the child table added so far in one go
await _dbContext.SaveChangesAsync();
// Too LATE. tableBar has already been updated.
tableBar.ChildTable = new ChildTable() { Name = "Too" };
I think you may be using EF incorrectly.
Try:
// Get the parent tables out of the database (without tracking). They're now not 'attached'.
var parents = _dbContext.ParentTable.AsNoTracking().Where(x => x.Name == "foo" || x.Name == "bar").ToList();
Then dispose of that _dbContext and do all of the work outside of the function where you're making your changes.
Then in your loop create a new instance of the Context for each ParentTable that you retrieve from the database, make the changes, call SaveChangesAsync, then dispose of the context.
Next loop, do the same again.
Keeping a single instance of _dbContext at the class module level might seem logical but it's a bit of a 'gotcha'. Best to create (and dispose) of contexts as required.
foreach ()
{
// instantiate context with 'using' to enforce disposal
using (var _dbContext = new DbContext())
{
// retrieve records to be updated (NOT with AsNoTracking())
// make updates to the 'attached' records
// save changes
// dispose of context ("using" will automatically call Dispose() at the closing brace
}
}
i tried this method that I created but it prompts me an error:
Realms.RealmInvalidObjectException:This object is detached. Was it deleted from the realm?'
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
using (var transaction = Realm.GetInstance(config).BeginWrite())
{
Realm.GetInstance(config).Remove(denom_list[0]);
transaction.Commit();
}
}
what is the proper coding for deleting records from database in realm in C# type of coding?
You are doing it the right way. The error message you are getting indicates that the object was removed already. Are you sure it still exists in the realm?
UPDATE:
I decided to update this answer because my comment on the other answer was a bit hard to read.
Your original code should work fine. However, if you want deleteFromDatabase to accept lists with CashDenomination instances that either have been removed already or perhaps were never added to the realm, you would need to add a check. Furthermore, note that you should hold on to your Realm instance and use it in the transaction you created. In most cases, you want to keep it around even longer, though there is little overhead to obtaining it via GetInstance.
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
if (!denom_list[0].IsValid) // If this object is not in the realm, do nothing.
return;
var realm = Realm.GetInstance(config);
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom_list[0]);
transaction.Commit();
}
}
Now, if you want to use identifiers, you could look it up like you do, but still just use Remove:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.All<CashDenomination>().FirstOrDefault(c => c.denom_id == denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
Finally, if your CashDenomination has denom_id marked as PrimaryKey, you could look it up like this:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.ObjectForPrimaryKey<CashDenomination>(denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
public void deleteFromDatabase(Realm realm, long cashDenominatorId)
{
realm.Write(() =>
{
var cashDenominator = realm.All<Person>().Where(c => c.Id == cashDenominatorId);
Realm.RemoveRange<CashDenomination>(((RealmResults<CashDenomination>)cashDenominator));
});
}
Which you would call as
Realm realm = Realm.GetInstance(config);
var denom_list = ...
// ...
deleteFromDatabase(realm, denom_list[0].id);
I already made it having this code :) thanks to #EpicPandaForce 's answer.
public void deleteFromDatabase(int denom_ID, int form_ID)
{
//Realm realm;
//and
//RealmConfiguration config = new RealmConfiguration(dbPath, true);
//was initialized at the top of my class
realm = Realm.GetInstance(config);
realm.Write(() =>
{
var cashflow_denom = realm.All<CashDenomination>().Where(c => c.denom_id == denom_ID);
var cashflow_form = realm.All<CashForm>().Where(c => c.form_id == form_ID);
realm.RemoveRange(((RealmResults<CashDenomination>)cashflow_denom));
realm.RemoveRange(((RealmResults<CashForm>)cashflow_form));
});
}
it is now deleting my data without exception :)
Error message: Attaching an entity of type failed because another entity of the same type already has the same primary key value.
Question: How do I attached an entity in a similar fashion as demonstrated in the AttachActivity method in the code below?
I have to assume the "another entity" part of the error message above refers to an object that exists in memory but is out of scope (??). I note this because the Local property of the DBSet for the entity type I am trying to attach returns zero.
I am reasonably confident the entities do not exist in the context because I step through the code and watch the context as it is created. The entities are added in the few lines immediately following creation of the dbcontext.
Am testing for attached entities as specified here:what is the most reasonable way to find out if entity is attached to dbContext or not?
When looking at locals in the locals window of visual studio I see no entities of type Activity (regardless of ID) except the one I am trying to attach.
The code executes in this order: Try -> ModifyProject -> AttachActivity
Code fails in the AttachActivity at the commented line.
Note the code between the debug comments which will throw if any entities have been added to the context.
private string AttachActivity(Activity activity)
{
string errorMsg = ValidateActivity(activity); // has no code yet. No. It does not query db.
if(String.IsNullOrEmpty(errorMsg))
{
// debug
var state = db.Entry(activity).State; // Detached
int activityCount = db.Activities.Local.Count;
int projectCount = db.Activities.Local.Count;
if (activityCount > 0 || projectCount > 0)
throw new Exception("objects exist in dbcontext");
// end debug
if (activity.ID == 0)
db.Activities.Add(activity);
else
{
db.Activities.Attach(activity); // throws here
db.Entry(activity).State = System.Data.Entity.EntityState.Modified;
}
}
return errorMsg;
}
public int ModifyProject(Presentation.PresProject presProject, out int id, out string errorMsg)
{
// snip
foreach (PresActivity presActivity in presProject.Activities)
{
Activity a = presActivity.ToActivity(); // returns new Activity object
errorMsg = ValidateActivity(a); // has no code yet. No. It does not query db.
if (String.IsNullOrEmpty(errorMsg))
{
a.Project = project;
project.Activities.Add(a);
AttachActivity(a);
}
else
break;
}
if (string.IsNullOrEmpty(errorMsg))
{
if (project.ID == 0)
db.Projects.Add(project);
else
db.AttachAsModfied(project);
saveCount = db.SaveChanges();
id = project.ID;
}
return saveCount;
}
This is the class that news up the dbContext:
public void Try(Action<IServices> work)
{
using(IServices client = GetClient()) // dbContext is newd up here
{
try
{
work(client); // ModifyProject is called here
HangUp(client, false);
}
catch (CommunicationException e)
{
HangUp(client, true);
}
catch (TimeoutException e)
{
HangUp(client, true);
}
catch (Exception e)
{
HangUp(client, true);
throw;
}
}
I am not asking: How do I use AsNoTracking What difference does .AsNoTracking() make?
One solution to avoid receiving this error is using Find method. before attaching entity, query DbContext for desired entity, if entity exists in memory you get local entity otherwise entity will be retrieved from database.
private void AttachActivity(Activity activity)
{
var activityInDb = db.Activities.Find(activity.Id);
// Activity does not exist in database and it's new one
if(activityInDb == null)
{
db.Activities.Add(activity);
return;
}
// Activity already exist in database and modify it
db.Entry(activityInDb).CurrentValues.SetValues(activity);
db.Entry(activityInDb ).State = EntityState.Modified;
}
Attaching an entity of type failed because another entity of the same type already has the same primary key value. This can happen when using the Attach method or setting the state of an entity to Unchanged or Modified if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the Add.
The solution is that
if you had to use GetAll()
public virtual IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
Change To
public virtual IEnumerable<T> GetAll()
{
return dbSet.AsNoTracking().ToList();
}
I resolved this error by changing Update method like below.
if you are using generic repository and Entity
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);
or normal(non-generic) repository and entity , then
_dbContext.Set<TaskEntity>().AddOrUpdate(entityToBeUpdatedWithId);
If you use AddOrUpdate() method, please make sure you have added
System.Data.Entity.Migrations namespace.
This is driving me nuts. I'm not sure what else to try. This is my latest attempt to update a list of objects within another object using EF 4.3.
The scenario is that a user has added a new Task to an Application that already has one task in its Tasks property. The Application is not attached to the DB context because it was retrieved in a prior logic/DB call. This is the class and property:
public class Application : EntityBase
{
public ObservableCollection<TaskBase> Tasks { // typical get/set code here }
}
This is my attempt to update the list. What happens is that the new Task gets added and the association correctly exists in the DB. However, the first task, that wasn't altered, has its association removed in the DB (its reference to the Application).
This is the Save() method that takes the Application that the user modified:
public void Save(Application newApp)
{
Application appFromContext;
appFromContext = this.Database.Applications
.Include(x => x.Tasks)
.Single(x => x.IdForEf == newApp.IdForEf);
AddTasksToApp(newApp, appFromContext);
this.Database.SaveChanges();
}
And this is the hooey that's apparently necessary to save using EF:
private void AddTasksToApp(Application appNotAssociatedWithContext, Application appFromContext)
{
List<TaskBase> originalTasks = appFromContext.Tasks.ToList();
appFromContext.Tasks.Clear();
foreach (TaskBase taskModified in appNotAssociatedWithContext.Tasks)
{
if (taskModified.IdForEf == 0)
{
appFromContext.Tasks.Add(taskModified);
}
else
{
TaskBase taskBase = originalTasks.Single(x => x.IdForEf == taskModified.IdForEf); // Get original task
this.Database.Entry(taskBase).CurrentValues.SetValues(taskModified); // Update with new
}
}
}
Can anyone see why the first task would be losing its association to the Application in the DB? That first task goes through the else block in the above code.
Next, I'll need to figure out how to delete one or more items, but first things first...
After continual trial and error, this appears to be working, including deleting Tasks. I thought I'd post this in case it helps someone else. I'm also hoping that someone tells me that I'm making this more complicated than it should be. This is tedious and error-prone code to write when saving every object that has a list property.
private void AddTasksToApp(Application appNotAssociatedWithContext, Application appFromContext)
{
foreach (TaskBase taskModified in appNotAssociatedWithContext.Tasks)
{
if (taskModified.IdForEf == 0)
{
appFromContext.Tasks.Add(taskModified);
}
else
{
TaskBase taskBase = appFromContext.Tasks.Single(x => x.IdForEf == taskModified.IdForEf); // Get original task
this.Database.Entry(taskBase).CurrentValues.SetValues(taskModified); // Update with new
}
}
// Delete tasks that no longer exist within the app.
List<TaskBase> tasksToDelete = new List<TaskBase>();
foreach (TaskBase originalTask in appFromContext.Tasks)
{
TaskBase task = appNotAssociatedWithContext.Tasks.Where(x => x.IdForEf == originalTask.IdForEf).FirstOrDefault();
if (task == null)
{
tasksToDelete.Add(originalTask);
}
}
foreach (TaskBase taskToDelete in tasksToDelete)
{
appFromContext.Tasks.Remove(taskToDelete);
this.Database.TaskBases.Remove(taskToDelete);
}
}
edit: in case anyone is wondering, the actionhandler invokes code that creates and disposes the same kind of datacontext, in case that might have anything to do with this behaviour. the code doesn't touch the MatchUpdateQueue table, but i figure i should mention it just in case.
double edit: everyone who answered was correct! i gave the answer to the respondent who suffered most of my questioning. fixing the problem allowed another problem (hidden within the handler) to pop up, which happened to throw exactly the same exception. whoops!
I'm having some issues with deleting items in LINQ. The DeleteOnSubmit call in the code below causes a LINQ Exception with the message "Cannot add an entity with a key that is already in use." I'm not sure what I'm doing wrong here, it is starting to drive me up the wall. The primary key is just an integer autoincrement column and I have no other problems until I try to remove an item from the database queue. Hopefully I'm doing something painfully retarded here that is easy to spot for anyone who isn't me!
static void Pacman()
{
Queue<MatchUpdateQueue> waiting = new Queue<MatchUpdateQueue>();
events.WriteEntry("matchqueue worker thread started");
while (!stop)
{
if (waiting.Count == 0)
{
/* grab any new items available */
aDataContext db = new aDataContext();
List<MatchUpdateQueue> freshitems = db.MatchUpdateQueues.OrderBy(item => item.id).ToList();
foreach (MatchUpdateQueue item in freshitems)
waiting.Enqueue(item);
db.Dispose();
}
else
{
/* grab & dispatch waiting item */
MatchUpdateQueue item = waiting.Peek();
try
{
int result = ActionHandler.Handle(item);
if (result == -1)
events.WriteEntry("unknown command consumed : " + item.actiontype.ToString(), EventLogEntryType.Error);
/* remove item from queue */
waiting.Dequeue();
/* remove item from database */
aDataContext db = new aDataContext();
db.MatchUpdateQueues.DeleteOnSubmit(db.MatchUpdateQueues.Single(i => i == item));
db.SubmitChanges();
db.Dispose();
}
catch (Exception ex)
{
events.WriteEntry("exception while handling item : " + ex.Message, EventLogEntryType.Error);
stop = true;
}
}
/* to avoid hammering database when there's nothing to do */
if (waiting.Count == 0)
Thread.Sleep(TimeSpan.FromSeconds(10));
}
events.WriteEntry("matchqueue worker thread halted");
}
You could do something to the effect of
db.MatchUpdateQueues.DeleteOnSubmit(db.MatchUpdateQueues.Single(theItem => theItem == item));
Just a note as other answers hinted towards Attach.. you will not be able to use attach on a context other then the original context the item was received on unless the entity has been serialized.
Try wrapping the entire inside of the while loop in a using statement for a single data context:
Queue<MatchUpdateQueue> waiting = new Queue<MatchUpdateQueue>();
events.WriteEntry("matchqueue worker thread started");
while (!stop)
{
using (var db = new aDataContext())
{
if (waiting.Count == 0)
{
/* grab any new items available */
List<MatchUpdateQueue> freshitems = db.MatchUpdateQueues
.OrderBy(item => item.id)
.ToList();
foreach (MatchUpdateQueue item in freshitems)
waiting.Enqueue(item);
}
...
}
}
Use:
aDataContext db = new aDataContext();
item = new MatchUpdateQueue { id=item.id }; // <- updated
db.MatchUpdateQueues.Attach(item);
db.MatchUpdateQueues.DeleteOnSubmit(item);
db.SubmitChanges();
Since you are using a new datacontext it doesn't know that the object is already in the db.
Remove the first db.Dispose() dispose. It can be the problem code because the entities keep a reference to their data context, so you don't want to dispose it while you are still be working with the instances. This won't affect connections, as they are open/closed only when doing operations that need them.
Also don't dispose the second data context like that, since an exception won't call that dispose code anyway. Use the using keyword, which will make sure to call dispose whether or not an exception occurs. Also grab the item from the db to delete it (to avoid having to serialize/deserialize/attach).
using (aDataContext db = new aDataContext())
{
var dbItem = db.MatchUpdateQueues.Single(i => i.Id == item.Id);
db.MatchUpdateQueues.DeleteOnSubmit(dbItem);
db.SubmitChanges();
}
try this if your TEntity's (here Area) Primary Key is of type Identity column;
Just it, without any change in your SP or Model:
public void InitForm()
{
'bnsEntity is a BindingSource and cachedAreas is a List<Area> created from dataContext.Areas.ToList()
bnsEntity.DataSource = cachedAreas;
'A nominal ID
newID = cachedAreas.LastOrDefault().areaID + 1;
'grdEntity is a GridView
grdEntity.DataSource = bnsEntity;
}
private void tsbNew_Click(object sender, EventArgs e)
{
var newArea = new Area();
newArea.areaID = newID++;
dataContext.GetTable<Area>().InsertOnSubmit(newArea);
bnsEntity.Add(newArea);
grdEntity.MoveToNewRecord();
}