I am using LINQ to insert records in the database. I create these records and keep track of them using a List. Based on some logic, I delete some of the records by deleting from the List. (I am using the same DataContext object).
When I want to insert the records in the database, I do the corresponding linq table's InsertOnSubmit() followed by SubmitChanges() on datacontext object. LINQ inserts the deleted-from-List records too along with the ones that are present in the list.
example:
//list to keep track of records to insert
List list
// add the records to list
list.add(some records)
//deleted last 2 records
list.remove()
//call InsertAllOnSubmit on the linq table passing the list object with records to insert
linqTable.InsertAllOnSubmit(list)
//call SubmitChanges on datacontext object
datacontext.SubmitChanges()
I came across this msdn article Object States and Change-Tracking (LINQ to SQL)
You can explicitly request Inserts by
using InsertOnSubmit. Alternatively,
LINQ to SQL can infer Inserts by
finding objects connected to one of
the known objects that must be
updated. For example, if you add an
Untracked object to an
EntitySet(TEntity) or set an
EntityRef(TEntity) to an Untracked
object, you make the Untracked object
reachable by way of tracked objects in
the graph. While processing
SubmitChanges, LINQ to SQL traverses
the tracked objects and discovers any
reachable persistent objects that are
not tracked. Such objects are
candidates for insertion into the
database.
I guess the question boils down to this - how do I change the deleted objects' state to 'Untracked'?
I tried DeleteOnSubmit after I delete the objects from list but that gives an exception (Cannot remove an entity that has not been attached).
Can someone please point me to a solution? Thanks.
I would like to know if I can achieve this using LINQ only. (I know that I can use an stored proc and insert only the records in the list.)
I think that the elements of your list are not being removed, because the List.Remove method determines equality using the default equality comparer.
If you don't want to write a custom comparer, I recommend you to use the RemoveAll method, which receives a predicate as its first parameter to match the elements that will be removed from the list:
list.RemoveAll( e=> /*condition to remove the element*/ );
Or the RemoveAt method, that removes the element based on the specified index:
list.RemoveAt(0); // Delete first element
Related
So im using EFBulkInsert https://efbulkinsert.codeplex.com/
The problem is I have child objects I need to set the ID of the inserted parent objects.
Previously after inserting the parents objects I've tried to rely on the context to return the id's and set them on the child objects - then use EFBulkInsert to insert the child objects - every now and then the context gets confused even after recreating the context and I get the wrong id on the child objects.
Does anyone have a good pattern / strategy for setting the parent id on the child object I should mention i'm doing this for a batch of 1000 parent objects. So I don't particularly want to get the id's from the database after SaveChanges for the parent object unless it's performant.
The best idea I have is to add two temporary columns, firtst to hold an original Id, and second to hold parentId. After bulk insert update proper columns. This method requires privilages to modify the table.
I had a similar problem when I couldn't modify database, so I set AutoDetectChangesEnabled and ValidateOnSaveEnabled to false, but results were not very satisfied.
In a data model like this (http://alanstorm.com/2009/img/magento-book/eav.png) I want to get the value from an EAV_Attribute using Linq to SQL.
Assuming that an EAV_Attribute only exists in one inherited table (varchar, decimal, int, etc.) how can I get it in a linq query?
I know that I can use the Inheritance for this, but I want to execute it in the SQL Database side...
Is it possible to do a kind of Coalesce in Linq, considering that the elements have different types?
EAV and linq is not a happy marriage. I think your best shot is to create an unmapped property in eav_attribute that resolves the value (as object) from it's typed attribute child. With entity framework, you won't be able to use this property in an expression (i.e. not in a Where or Select), You must convert to IEnumerable first to access it. (Linq-to-sql may allow it because it can switch to linq-to-objects below the hood).
Another option is to create a calculated column of type sql_variant that does the same, but now in t-sql code. But... EF does not suport sql_variant. You've got to use some trickery to read it.
That's the reading part.
For setting/modifying/deleting values I don't see any shortcuts. You just have to handle the objects as any object graph with parents and children. In sql server you can't use cascaded delete because it can only be defined for one foreign key. (This may tackle that, but I never tried).
So, not really good news, I'm afraid. Maybe good to know that in one project I also work with a database that has an inevitable EAV part. We do it with EF too, but it's not without friction.
First of all, I recommend using TPH and not TPT for EAV tables. (One table with multiple nullable value columns (one per type) + discriminator vs. one table per type.)
Either way, if you modelled the value entity as an abstract class (containing the two IDs) with an inheriting entity per value data type that adds the value property, then your LINQ should look like this:
var valueEntity = context.ProductAttributes.Where(pa =>
pa.ProductId == selectedProductId
&& pa.AttributeTypeId == selectedAttributeTypeId)
.SingleOrDefault() as ProductAttributeOfDouble;
if valueEntity != null
return valueEntity.Value;
return null;
Where the entity types are: Product, AttributeType, ProductAttribute, ProductAttributeOfDouble, ... ProductAttributeOfString.
I'm doing this on a table with ~43k rows:
MyDbContext.Stores.Load();
MyDbContext.Stores.Local.Count.Dump(); //horrible performance!
I can see through the profiler that the first instruction fires up the select statement to fetch all rows. Actually the second instruction returns the correct value but after ~12 seconds, and it is not what I was expecting considering that all data should be in memory.
What is wrong (or what is its real purpose) with .Local in Entity Framework?
I think you should do this:
var stores = MyDbContext.Stores.ToList();
// stores is in memory after executing .ToList()
var count = stores.Count();
DbSet.Local Property
This property returns an ObservableCollection that contains all
Unchanged, Modified, and Added objects that are currently tracked by
the context for the given DbSet. The returned observable collection
stays in sync with the underlying DbSet collection and the contents of
the context. This means that you can modify the observable collection
or add/remove entities to/from the underlying DbSet collection (that
includes adding entities by executing a query) and both collections
will be synchronized.
This property is often used in data binding applications.
There have been numerous questions posed on this site relating to the retrieval of the IDENTITY after an insert is performed. The way we have been getting the identity is to make the call below, immediately after calling SaveChanges();
context.MyClass.OrderByDescending(c => c.Id).FirstOrDefault();
This seems to work consistently may be completely adequate; however, it has the appearence of opening up a potential for error, should another record be added in between the calls. So the first question is, given that EF performs withing a transacional context, is this method sound?
Secondly, the answer provided to the following question suggests there may be a better way.
Linq to SQL - How to find the the value of the IDENTITY column after InsertOnSubmit()
In that answer, after calling SubmitChanges(), the following call (where "tst" represents the user's class) retrieves the value.
Response.Write("id:" + tst.id.ToString)
This appears to work exactly the same way in LINQ to Entities, where after the call to save changes the instance of the class now includes the id.
context.MyClass.Add(myClass);
context.SaveChanges();
int myNewIdentity = myClass.Id;
Since we are asking for the the actual ID of the class instance (actual record) it would appear to be failsafe. And, it seems logical that the designers of EF should make such basic functionality available. Can anyone confirm that this is proper way to get the identity or at least a best practice?
Yes, LINQ-to-Entities (and LINQ-to-SQL for that matter) will set the generated identity column back in the entity for you after SaveChanges is called. It will also do so for any foreign keys that couldn't be set ahead of time (for instance, a new parent row + a new child row are saved together, and after SaveChanges you'll have the right value in the child row's FK value).
Your particular concern is documented in the 'Working with Entity Keys' page:
http://msdn.microsoft.com/en-us/library/dd283139.aspx
The particular section is 'Entity Keys and Added Objects' and the particular steps are:
4 - If the INSERT operation succeeds, server-generated values are written back to the ObjectStateEntry.
5 - The ObjectStateEntry updates the object with the server-generated value.
I am using Sql tables without rowversion or timestamp. However, I need to use Linq to update certain values in the table. Since Linq cannot know which values to update, I am using a second DataContext to retrieve the current object from database and use both the database and the actual object as Input for the Attach method like so:
Public Sub SaveCustomer(ByVal cust As Customer)
Using dc As New AppDataContext()
If (cust.Id > 0) Then
Dim tempCust As Customer = Nothing
Using dc2 As New AppDataContext()
tempCust = dc2.Customers.Single(Function(c) c.Id = cust.Id)
End Using
dc.Customers.Attach(cust, tempCust)
Else
dc.Customers.InsertOnSubmit(cust)
End If
dc.SubmitChanges()
End Using
End Sub
While this does work, I have a problem though: I am also using StoredProcedures to update some fields of Customer at certain times. Now imagine the following workflow:
Get customer from database
Set a customer field to a new value
Use a stored procedure to update another customer field
Call SaveCustomer
What happens now, is, that the SaveCustomer method retrieves the current object from the database which does not contain the value set in code, but DOES contain the value set by the stored procedure. When attaching this with the actual object and then submit, it will update the value set in code also in the database and ... tadaaaa... set the other one to NULL, since the actual object does not contain the changed made by the stored procedure.
Was that understandable?
Is there any best practice to solve this problem?
If you make changes behind the back of the ORM, and don't use concurrency checking - then you are going to have problems. You don't show what you did in step "3", but IMO you should update the object model to reflect these changes, perhaps using OUTPUT TSQL paramaters. Or; stick to object-oriented.
Of course, doing anything without concurrency checking is a good way to lose data - so my preferred option is simply "add a rowversion". Otherwise, you could perhaps read the updated object out and merge things... somehow guessing what the right data is...
If you're going to disconnect your object from one context and use another one for the update, you need to either retain the original object, use a row version, or implement some sort of hashing routine in your database and retain the hash as part of your object. Of these, I highly recommend the Rowversion option as well. Using the current value as the original value like you are trying to do is only asking for concurrency problems.