Query Context for Inserted but yet Uncommitted records - c#

I've searched and searched stackoverflow and www, but have found no answers to this question.
I am looping through a number of records and under certain conditions I'm inserting new records into table A. Then I'm looping again on another data source (which cannot be merged with the first one), and if that be the case, I want to insert new records into the same table A. I only want to commit the records at the end of the process, but it'll give a primary key violation error if I just insert them.
Note: linq is not managing primary keys. Probably because I'm sort of a noob with linq and don't really know how to get linq to work with Oracle sequences.
My question is how do I check the existing context for the records I have inserted. This is what I am doing.
foreach(var rec in recordList1)
{
...
dataContext.InsertOnSubmit(obj);
}
foreach(var rec in recordList2)
{
if ( ! [check context here for existing record] )
{
...
dataContext.InsertOnSubmit(obj);
}
}
dataContext.SubmitChanges();
I've tried querying the context in different ways, but it'll only return committed values.
Thanks in advance!
Best regards.

To access the objects inserted, updated, deleted in the datacontext you need to call GetChangeSet.
var changed = dataContext.GetChangeSet();
var inserted = changed.Inserts;
var updated = changed.Updates;
var deleted = changed.Deletes;

Related

Need to get the data during an SaveChanges() within EF Core

I need data for a LINQ query that is not already saved to the database. Here is my code:
foreach (var item in BusProc)
{
var WorkEffortTypeXref = new WorkEffortTypeXref
{
SourceDataEntityTypeName = "BusProc",
SourceDataEntityTypeId = item.BusProcId,
};
_clDBContext.WorkEffortTypeXref.AddRange(WorkEffortTypeXref);
}
but I need this data in the SQL Database before I do a join LINQ query on the data. Although I don't really want to do a OnSave() after this function because I want to make the whole process transactional.
This is the LINQ I need to execute. What is the best way to do this?
var linqquery = from bupr in BusProc
join wrtx in WorkEffortTypeXref on bupr.BusProcId equals wrtx.SourceDataEntityTypeId
// where wrtx.SourceDataEntityTypeName == "BusProc"
select new
{
wrtx.TargetDataEntityTypeId
};
First, try to compile your code. This code won't compile and as-is isn't comprehensible. You are treating WorkEffortTypeXref as a class, a singular object and a list all in the first section of code, so we really can't know what it is supposed to be.
Now, as I understand your question, you want to query information that is being added to a table, (currently stored in memory as a collection of some sort) but you want to query it before it is added? What if the table has other rows that match your query? What if the insert violates a constraint and therefore fails? Linq can query an in-memory collection, but you have to choose, are you querying the collection that you have in memory (and isn't yet a row of your table) or the database (which has all of the rules/contstraints/etc that databases provide)? Until the records are saved to your table, they aren't the same thing.

How To insert records of one database to other database in LINQ

I have 2 database with tables.
I wanted to insert records from first database to second database table in LINQ. I have created 2 dbml files with 2 datacontexts but I am unable to code the insertion of records.
I have list of records:
using(_TimeClockDataContext)
{
var Query = (from EditTime in _TimeClockDataContext.tblEditTimes
orderby EditTime.ScanDate ascending
select new EditTimeBO
{
EditTimeID = EditTime.EditTimeID,
UserID = Convert.ToInt64(EditTime.UserID),
ScanCardId = Convert.ToInt64(EditTime.ScanCardId),
}).ToList();
return Query;
}
Now I want to insert record in new table which is in _Premire2DataContext.
If you want to "copy" records from one database to another using Linq then you need two database contexts, one for the database you are reading from, and one for the database you are reading to.
EditTime[] sourceRows;
using (var sourceContext = CreateSourceContext())
{
sourceRows = ReadRows(sourceContext);
}
using (var destinationContext = CreateDestinationContext())
{
WriteRows(destinationContext, sourceRows);
}
You now just need to fill in the implementations for ReadRows and WriteRows using standard LINQ to SQL code. The code for writing rows should look a bit like this.
void WriteRows(TimeClockDataContext context, EditTime[] rows)
{
foreach (var row in rows)
{
destinationContext.tblEditTimes.Add(row);
}
destinationContext.SubmitChanges();
}
Note that as long as the schema is the same you can use the same context and therefore the same objects - so when reading records we ideally want to return the correct array type, therefore reading is going to look a bit like this
EditTime[] ReadRows(TimeClockDataContext context)
{
return (
from EditTime in _TimeClockDataContext.tblEditTimes
orderby EditTime.ScanDate ascending
select EditTime
).ToArray();
}
You can use an array or a list - it doesn't really matter. I've used an array mostly because the syntax is shorter. Note that we return the original EditTime objects rather than create new ones as this means we can add those objects directly to the second data context.
I've not compiled any of this code yet, so it might contain some typos. Also apologies if I've made some obvious errors - its been a while since I last used LINQ to SQL.
If you have foreign keys or the second database has a different schema then things get more complicated, but the fundamental process remains the same - read from one context (using standard LINQ to SQL) and store the results somewhere, then add the rows the the second context (using standard LINQ to SQL).
Also note that this isn't necessarily going to be particularly quick. If performance is an issue then you should look into using bulk inserts in the WriteRows method, or potentially even use linked servers to do the entire thing in SQL.

Updating a record using Linq-to-SQL

I do my query...
var result = from u in tdc.tblUsers
where u.UserID == userID
select u;
and then I change the values I want to:
foreach (tblUsers u in result)
{
//change values (and no im not changing the primary key or foreign keys)
}
then I submit changes
tdc.SubmitChanges();
When it hits submit changes, it throws exception that the row wasn't found or was changed. I am the only person using this so there's no other conflicts with accessing the db or locking. Why would it throw the ChangeConflictException? I have stepped through with debugger and the data persists all the way through the process, including the changes that I'm trying to make.
I have also tried it this way before and got the same error
tblUsers result = (from u in tdc.tblUsers
where u.UserID == userID
select u).Single();
result.Address = address;
result.Phone = phone;
tdc.SubmitChanges();
It will only ever retrieve 1 record with this query since UserID is the primary key.
I've done this many times and it has worked. Every example I've found is exactly what I have.
Maybe you work with different context? Try to encapsulate it by using
using (myContext ctx = new myContext())
{
var user = ctx.users.first();
user.name="blah";
ctx.SubmitChanges();
}
Quite often, if you look at the actual SQL that Linq-to-SQL generates, you'll see that it is being really over-zealous with matching the database row that you initially retrieved. Imagine that you have a table with columns ID(PK), A, B, C. You would think that if you updated column C for a row, it should be sufficient to update the row with a matching primary key. But what often happens is that Linq-to-SQL is also trying to match on columns A and B as well. Normally, that's fine. Unless you have concurrent writes, either from multi-threading or multi-processes, and something else modifies column A or B for the record that you are trying to update. Then you get these System.Data.Linq.ChangeConflictException: Row not found or changed. errors when you call SubmitChanges() on your data context.

Linq: Delete and Insert same Primary Key values within TransactionScope

I want to replace existing records in the DB with new records in one transaction. Using TransactionScope, I have
using ( var scope = new TransactionScope())
{
db.Tasks.DeleteAllOnSubmit(oldTasks);
db.Tasks.SubmitChanges();
db.Tasks.InsertAllOnSubmit(newTasks);
db.Tasks.SubmitChanges();
scope.Complete();
}
My program threw
System.InvalidOperationException: Cannot add an entity that already exists.
After some trial and error, I found the culprit lies in the the fact that there isn't any other execution instructions between the delete and the insert. If I insert other code between the first SubmitChanges() and InsertAllOnSubmit(), everything works fine. Can anyone explain why is this happening? It is very concerning.
I tried another one to update the objects:
IEnumerable<Task> tasks = ( ... some long query that involves multi tables )
.AsEnumerable()
.Select( i =>
{
i.Task.Duration += i.LastLegDuration;
return i.Task;
}
db.SubmitChanges();
This didn't work neither. db didn't pick up any changes to Tasks.
EDIT:
This behavior doesn't seem to have anything to do with Transactions. At the end, I adopted the grossly inefficient Update:
newTasks.ForEach( t =>
{
Task attached = db.Tasks.Single( i => ... use primary id to look up ... );
attached.Duration = ...;
... more updates, Property by Property ...
}
db.SubmitChanges();
Instead of inserting and deleting or making multiple queries, you can try to update multiple rows in one pass by selecting a list of Id's to update and checking if the list contains each item.
Also, make sure you mark your transaction as complete to indicate to transaction manager that the state across all resources is consistent, and the transaction can be committed.
Dictionary<int,int> taskIdsWithDuration = getIdsOfTasksToUpdate(); //fetch a dictionary keyed on id's from your long query and values storing the corresponding *LastLegDuration*
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
var tasksToUpdate = db.Tasks.Where(x => taskIdsWithDuration.Keys.Contains(x.id));
foreach (var task in tasksToUpdate)
{
task.duration1 += taskIdsWithDuration[task.id];
}
db.SaveChanges();
scope.Complete();
}
Depending on your scenario, you can invert the search in the case that your table is extremely large and the number of items to update is reasonably small, to leverage indexing. Your existing update query should work fine if this is the case, so I doubt you'll need to invert it.
I had same problem in LinqToSql and I don't think its to do with the transaction, but with how the session/context is coalescing changes. I say this because I fixed the problem by bypassing linqtosql for the delete and using some raw sql to do it. Ugly I know, but it worked, and all inside a transaction scope.

Linq To Sql ManyToMany Update Existing Records

I have a many to many relationship between 2 entities.
UserInfo and Campaign and the third table UserInfoCampaigns
On the front end a user should be able to update the user campaigns by using multiple checkboxes.
So I have:
var selectedCampaignsIds = new int[] {1,2,3}
var user = _extendedUserService.GetUser(new Guid(userId));
var newCampaigns =_campaignrepository.GetAllCampaignsByIds(selectedCampaignsIds);
I can assign newly selected campaigns by following:
foreach (var campaign in newCampaigns)
{
var userInfoCampaign = new UserInfoCampaign { UserInfoId = user.Id, CampaignId = campaign.Id };
_campaignrepository.SaveUserInfoCampaign(userInfoCampaign);
}
My question is how do I update UserInfo campaigns taking into consideration that a user might have or might not have existing campaigns?
Do I delete all existing UserInfoCampaigns and then reassign with new ones? Or there is a more optimal way of updating the UserInfo campaigns without deleting them first using LINQ to SQL?
Thanks a lot.
Generally, you'd delete them and recreate them. That's the way I handle this kind of situation of many to many.
If you went for (Delete all) solution then you may consider that the ids will increase in your table in none continues form (I'm assuming that your PK is an int), if you dont care about that then delete them then re-add
if you want the identity to be in a continues form you may set a flag (IsDeleted) in your campaigns table that refers to the campaign as deleted, you set it to ture when user delete a campaign and set it false when user re-select it from the UI this insure less more records but take a more bit work in your code
Generally, we just let SQL handle it. You can specify ON DELETE on the relationships in SQL, you can CASCADE or SET DEFAULT or SET NULL depending on what behaviour you want.
Here's an example for ON DELETE CASCADE:
http://www.codeproject.com/KB/cs/CascadingDeletes_cs.aspx

Categories

Resources