LINQ to SQL and attaching Entities
I am playing around with LINQ to SQL in conjunction with business objects. So rather than using LINQ directly in the front end UI I want an abstraction layer. One problem that I can't seem to resolve with the data context is how to update Entities based on POCO objects that didn't originate out of the DataContext. LINQ wants to manage all objects through its data context which basically manages change tracking for any entities - any changes you make can later be persisted.
That's fine if you use LINQ directly in your UI code but it's not all that great if you use LINQ in the middle tier. Incidentally Rob Connery also mentioned this in his LINQ SonicCast last week. The idea is that you have wrapper methods for CRUD (and all other data access) in the business layer so you'd write code like this:
busCustomerBasic Customer = new busCustomerBasic();
tt_customer cust = Customer.Load(1);if (cust == null)
{Response.Write(Customer.ErrorMessage);
return;}
cust.Address = "43 Kaiea Place " + DateTime.Now.ToString();
if ( !Customer.Update(cust) ){Response.Write(Customer.ErrorMessage);
return;}
True this doesn't look much different than using the LINQ objects directly but the big difference is that you get some level of abstraction for the load and update code so you can inject things like tracing and error handling etc.
So to implement you might use code like this for the Load:
public tt_customer Load(int pk)
{ try { tt_customer cust = context.tt_customers.Single(c => c.Pk == pk);if (cust == null)
{this.SetError("Invalid Pk");
return null;
}
return cust;}
catch (Exception ex)
{ this.SetError(ex);return null;
}
return null;
}
and another routine later on that saves this object (assuming that possibly the context has gone away). There's a method called Attach provided on the ITable instance that supposedly allows re-attaching an instance to be change tracked. Unfortunately I can't get this to work:
public bool Update(tt_customer customer)
{context = new TimeTrakkerContext();
tt_customer Tcust = context.tt_customers.Single(c => c.Pk == customer.Pk);context.tt_customers.Attach(customer, Tcust);
context.SubmitChanges();
return true;
}
The code compiles but at runtime it complains:
Cannot add an entity with a key that is already in use.
Huh? Am I not telling LINQ to compare the two objects and assume the differences to be changes? That's what I would expect from an Attach method that takes two object parameters.
I also tried:
context.tt_customers.Attach(customer, true);
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
Nope doesn't like that either. You've got to be kidding, right? How hard could it possibly be to reattach an entity that is actually mapped by the entity manager? The manager knows all the fields involved and at the very least should be able to create an update statement that checks everything or in the case of comparing two entitities and figuring out the differences. Nothing a little Reflection code can't fix.
I can use this code and it won't bomb outright:
context.tt_customers.Attach(customer);
But it also doesn't do anything on the server. It basically looks at the object and sees no changes related to the current context so it doesn't update anything. IOW, it attaches and enables change tracking but only from this point forward. But even that doesn't work. If you do:
context.tt_customers.Attach(customer);
customer.Address = customer.Address;
you now get:
Cannot add an entity with a key that is already in use.
again.
So, is this the way it's supposed to work or a bug? I would expect that if you pass in a context loaded object to compare against, that LINQ should be able to reestablish proper update status.
I just don't understand what could possibly be so hard about re-attaching an object. The Context/Tables hold all of the information necessary to figure out what's changed and it would be a fairly trivial matter - using Reflection or simply using a full field SQL Update statement to get updates to go to the server.
And yes there is a potential workaround for this particular issue for some scenarios which involves just letting the business object hang on to the DataContext, which frankly is the more common case. So rather than doing an update as above you'd write say a Save() method that does:
public bool Save()
{ try {context.SubmitChanges();
}
catch(Exception ex)
{ this.SetError(ex);return false;
}
return true;
}
That works *as long as you hang on to the business object*.
Now although I don't think this is great it's not as terrible as it might sound given that the DataContext is not actually connected to the database - all data access is handled atomically by the DataContext connection and disconnecting, but you still have to deal with this persistence. If you retrieve an object from some external source you're downright out of luck.
Using the connected state is also bad news for New entries which have to be explicitly added with Table.Add() and a new object. This means the burden of determining whether you're dealing with new entity or updating an existing one is really up to you, which is also sucky - LINQ to SQL doesn't figure that out automatically.
CLUNQ
The more I look at LINQ the more I'm coming to the conclusion that using LINQ in a middle tier - especially in a generic business object architecture - is not going to work well. There are many little problem issues that when all added up point at more problems being created than solved by the entity generation and easy CRUD layer.
Hopefully I'm just being dense and there are some workarounds for some of these issues, but reading a number of other posts on this 'detached' issue at least it doesn't appear so... Apparently even the ADO.NET Entity framework doesn't address this issue. <shrug>
The Voices of Reason
# re: LINQ to SQL and attaching Entities
I'm sure that what we've been trying to do will be covered in RTM... well I'm not sure but strongly hopeful. Great post Rick!
# re: LINQ to SQL and attaching Entities
>>
If a timestamp or a version number column is used for optimistic concurrency check, then the corresponding member must be set before calling Attach(). Values for other members need not be set before calling Attach(). LINQ to SQL uses minimal updates with optimistic concurrency checks; i.e. a member that is not set or checked for optimistic concurrency is ignored.
<<
I had tried that but even with a timestamp field Attach fails complaining that the PK already exists(when providing an existing entity). I added a timestamp field to all tables - re-added them, checked the generated classes that the timestamp field is recognized as a timestamp. Attach still doesn't seem to work here it continues to fail with the Pk exists already.
This doesn't sound much like a good solution as it requires the database to be explicitly set up to support this format. And why should this be necessary? The client knows the PK of the item so it can do its own comparison either with the WHERE clause or when an object is provided by comparing properties.
Wouldn't it make much more sense if:
Attach() could sync two objects (ie. read an instance from the database and compare properties), or otherwise assume that everything has changed and needs to be updated (ie. the DataAdapter 1.x model)?
# re: LINQ to SQL and attaching Entities
So, I make an update to an object, let's say from that example in word document:
c2.ContactName = "Steve"; in my webform.
User hits submit. I pass this object to a webservice. Inside the webservice I attach the object.
Now I have to 'replay' and set the value again???
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
http://www.west-wind.com/WebLog/posts/33570.aspx
But that's maybe not as critical as you can often get away with either passing types back as explicit types, creating custom types or in binding scenarios you don't even need the type information.
The real killer in Web applications though is DataBinding and the use of Reflection. LINQ entity lists binding are 3-4 times slower binding than datareader and 3 times+ slower than DataSets even. Reflection is a killer in this scenario.
# re: LINQ to SQL and attaching Entities
Client:
WebService.UpdateCustomer(customer);
Web service:
public bool UpdateCustomer(customer)
{
Customer c = Customer.Load(customer.CustomerID);
c.Copy(customer);
return c.Save();
}
I believe LINQ will only update properties that have changed, so even if you set them again they won't be sent in the update.
# re: LINQ to SQL and attaching Entities
Re: anonymous types, it's not so much that it can't be worked around, but we'll be finding ourselves not being able to get past the guilt of implementing Linq to Sql when we can't aren't leveraging anonymous types and jumping through hoops to get real middle tier integration.
Makes me wonder if MS is gearing Linq to Sql toward the "Strongly Typed Data Adapter" crowd :-) I was hoping for a balls to the wall feature and not more excuses to litter presentation layer with provider-specific code.
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
Attach is meant for adding new instances of objects to a new instance of a DataContext after the objects have been deserialized from another tier or equivalent. It is not meant for detaching and reattaching the same instances to the same context (or even a different context.) Doing so is strictly forbidden and if the DataContext could consistently tell that you were doing this it would throw an exception, but it can't so it doesn't.
If you want to make an update with data coming from another tier, you have to tell the DataContext how to do it. Calling Attach with just an object instance only provides enough information to let the DataContext pretend the object was just materialized into an unmodified state (as if by querying.) That's why after doing so no updates occur.
LINQ to SQL, by default, uses optimistic concurrency to perform updates and deletes. That means it needs to verify that the database state has not changed before it can perform either. To do so, it needs both the original state of the object and the newly changed state. An overload of Attach lets you specify two object instances, one in the original state and one in the new state. It is also possible to tell LINQ to SQL to 'just update all the fields', you do this with the Attach overload that takes a boolean second parameter. However, in order for optimistic concurrency to work there still must be some 'original' state to prove the database state has not changed. This is where the timestamp column comes in. If your object defines one you are good to go; that's all the original state you need. If you don't then optimistic concurrency cannot be performed, so you get an exception. If you don't care about that, and just want to blast the data into the database you have to opt out of optimistic concurrency by changing the UpdateCheck setting in the mapping to UpdateCheck.Never (its per column.)
# re: LINQ to SQL and attaching Entities
The numbers pretty much scaled down in this ratio even with smaller pages, and while it probably is not a huge issue on small pages the ration of 3 to 1 remained. Under load test numbers of data reader requests where nearly 3 times higher compared to equivalen LINQ code.
To be fair that's not LINQ's problem - that's databinding against objects which has to use Reflection for everything. Even lowly datasets perform way better because data storage is in collections that can be accessed quicker than properties via Reflection.
<shrug>
Just one more reason why it would be nice if a LINQ query could be translated into a DataReader/DataTable result without first going into Entities.
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
/// <summary> /// Updates a MediaType /// </summary> /// <param name="mediaType">MediaType Entity</param> [OperationBehavior(TransactionScopeRequired=false)] public void UpdateMediaType(MediaType mediaType) { // a local instance of the related data context InformationDataContext context; // if the entity is NOT contained in the table // of an instance-level property initialized on first get if (!this.InformationDataContext. MediaType.Contains(mediaType)) { // it is OK to set the local instance // to the instance-level variable context = this.InformationDataContext; } else { // if the item IS found it needs to be managed // using a new instance of the data context context = new InformationDataContext( ConfigurationProvider.ConnectionString); } // Only with the following implementation // Does this line or calls to any of the overloads // Successfully persist changes in the manner intended context.MediaType.Attach(mediaType, true); // Oh, but hooray! We can call "Submit Changes" // and experience an abstraction-level once removed // from our dreaded database platform context.SubmitChanges(); }
So what we are saying here is that we have successfully communicated back to our middle tier and hold reference to the object (in this case an entity called a MediaType managed by an InformationDataContext : DataContext) that we want to update but specifically SINCE this reference is held by an object that remains alive we CANNOT access that reference and MUST create a new instance of the associated data context????
I dont think so. And if there is anything close to a database query behind that new data context to look up the associated original value - well that is just sad. Equally so is any claim that this is proper functionality and somehow due to LINQ's candidacy for some particular usage not applicable to given examples.
# re: LINQ to SQL and attaching Entities
LinQ to SQL doesn't allow nice connection pooling mechanisms either, though I can do this myself by passing the connections into the context at creation I would have thought that competing with java's Persistence layer that this would be covered off.
Given that this is a real PITA, I did manage to get LinQ to work correctly after adding a timestamp (rowversion) field. Honestly, I don't normally work with SQL server and haven't encounted a rowversion before.
So here goes.
1. Create your table with a rowversion field, I allow null, though I'm not sure if this is required or not.
2. Drag your table to the designer in VS, or hand craft it if you aren't lazy
3. Set ALL fields "Update Check" to "Never"
4. for your "Version" field, set "Auto generated value" to "True" (again, not sure if this is required), set "Nullable" to true, "Time Stamp" to "True" and "Type" to "System.Data.Linq.Binary".
5. Implement your service similar to the following:
/// <summary> /// Save the product group and return a new copy with updated values. /// </summary> /// <param name="group">Product group to save.</param> /// <returns>Product group with new saved state.</returns> public ProductGroup UpdateProductGroup(ProductGroup group) { using (FilmLibraryDataContext ctx = new FilmLibraryDataContext(connectionString)) { // Attach the group to the newly formed context as modified. ctx.ProductGroups.Attach(group, true); // May raise an exception upon a conflict if the row has been // modified or deleted. ctx.SubmitChanges(); return group; } }
6. This will allow optimistic concurency, though you will need the client to track changes (my prefference as you should only require checking differences when conflicts occur).
Please if anyone knows how to NOT use the "rowversion/timestamp" datatype I'd be very interested. My preference is to use a simple "int" data type that is quite simply incramented on Updates and set to 1 on an insert, more than adequate. DateTime values run the risk of DLS changes and I'm not sure how this affects "timestamp" datatypes. This makes it easy for a client to request periodic updates by passing a dictionary of <int id,int ver> getting a returned list of updated objects.
I hope this helps anyone else in the same boat.
# re: LINQ to SQL and attaching Entities
I'd recommend checking out the the ADO.net Entity Framework for use with LINQ rather than forcing it.
# re: LINQ to SQL and attaching Entities
Linq to Entities isn't here yet and probably won't be for a while and when this post was actually made L2E had the same fucked up behavior.
Only after people started complaining about L2Sql did the Entity Framework folks relent and look into makeing this actually happen for the EF. And even the model they have now is a tack on approach at best...
# re: LINQ to SQL and attaching Entities
Like this:
public bool Update(tt_customer customer) {
context = new TimeTrakkerContext();
context.tt_customers.Attach(customer, GetCustumerById(custumer.Pk));
context.SubmitChanges();
return true;
}
public tt_custumer GetCustumerById(int id) {
context = new TimeTrakkerContext();
return context.tt_customers.Single(c => c.Pk == id);
}
That way I have no more problems with "Cannot add an entity with a key that is already in use"
Cya
# re: LINQ to SQL and attaching Entities
Thanks a lot,
I reference this article from my blog: http://blog.pampanotes.com/2008/02/first-thoughts-on-designing-linq.html
# re: LINQ to SQL and attaching Entities
Did you ever solve that? Linq and WCF just don't seem to play very well together at all.
# re: LINQ to SQL and attaching Entities
Thanks.
# re: LINQ to SQL and attaching Entities
my code looks like this
foreach (DLinq.Competence competence in competences)
{
DLinq.Competence c = NetcampusDB.Competences.SingleOrDefault(x => x.ID == competence.ID);
if (c == null)
{
NetcampusDB.Competences.InsertOnSubmit(competence); //this works fine for the first time and empty database got filled
}
/*but after this if the database is not empty, for the update this attach method doesnt work and says "cannot add an entity with a key that is already in use */
else
{
NetcampusDB.Competences.Attach(competence, GetCompetence(competence.ID));
}
}
NetcampusDB.SubmitChanges();
where i have
private DLinq.Competence GetCompetence(int id)
{
DLinq.Competence c = NetcampusDB.Competences.Single(x => x.ID == id);
return c;
}
kindly help me in this regard! Thanks in advance!
# re: LINQ to SQL and attaching Entities
http://community.omnicom.no/blogs/lea/archive/2008/04/25/updating-objects-with-linq-on-a-different-datacontext-instance.aspx
# re: LINQ to SQL and attaching Entities
P.S. Rick you have a great blog
# re: LINQ to SQL and attaching Entities
<code lang="c#">
public partial class DataClassesDataContext
{
public static DataClassesDataContext Instance
{
get
{
if (HttpContext.Current.Items["DataClassesDataContext"] == null)
HttpContext.Current.Items["DataClassesDataContext"] = new DataClassesDataContext();
return HttpContext.Current.Items["DataClassesDataContext"] as DataClassesDataContext;
}
}
}
</code>.
Then use that instance for all queries made. If you store objects in session then you may want to store the instance in session as well. I then just define a Save method on the objects like so:
<code lang="c#">
public void Save()
{
try { DataClassesDataContext.Instance.TableName.InsertOnSubmit(this); }
catch (Exception) {}
DataClassesDataContext.Instance.SubmitChanges();
}
</code>.
This approach falls down from a logical standpoint if you assign a value to an object loaded from the database that you don't want saved but call save on another object. But I haven't done that on any of the asp.net sites I work on.
# re: LINQ to SQL and attaching Entities
And MORE, when you have an array of serialized "Products" entity for example, including or not the same value for ProductID, i get an exception :
CODE:
List<Product> list = Deserialize<List<Product>>( xmlString )
// list include 2 items with ProductID = 1 for both of them
using( Northwind db = new ... )
{
// OF COURSE, TO USE THIS, I SET IsVersion on ProductID column for instance (you can create a much more specific database column Timestamp or version)
// It's fascinating because i only want to use the IsPrimaryKey in order to update my entity but LINQ refuses to
// work in such way except with IsVersion
db.Products.Attach( list[ 0 ], true ); // list[ 0 ].ProductID is 1
db.SubmitChanges(); // IS OK
// BUT THE FOLLOWING IS NOT OK (exception raised)
db.Products.Attach( list[ 1 ], true ); // list[ 0 ].ProductID is 1 also
db.SubmitChanges(); // IS NOT OK = "Cannot add an entity with a key that is already in use."
}
So how can i do, one process serialized many records (including sometimes the same ProductID record with up to date values of course) and the other process cannot read the serialization string in order to save them EASILY. It's really fascinating.
I searched how to disable the DataContext cache.... No success....
I tried with ObjectTrackingEnabled to false but Attach() raises an error.... No success
I found a solution, I create one DataContext per record to update in database....It's not perfect but it works as expected. LINQ and perfomance, are you kidding !!!!
I will appreciate a functions like these :
Attach( Entitiy, bool replaceEntityInCache )
Attach( Entity, bool useOnlyPrimaryKey )
.....
I have the feeling that LINQ is very poor when you work with disconnected record.
What a pity ?
# re: LINQ to SQL and attaching Entities
I had the same problem in my application. I resolved this issue by initializing the datacontext object in each iteration. Check the following code.
TimeManagerDataAccessDataContext objTimeManagerDataAccessDataContext = new TimeManagerDataAccessDataContext();
public void AddNewSupportingDocument(IEnumerable<tblSupportingDocument> objSupportingDocument ,int intStatusId) { if (objSupportingDocument != null) { DeleteDocuments(intStatusId); foreach (var SupportDocument in objSupportingDocument) { objTimeManagerDataAccessDataContext = new TimeManagerDataAccessDataContext(); objTimeManagerDataAccessDataContext.GetTable<tblSupportingDocument> ().InsertOnSubmit(SupportDocument); objTimeManagerDataAccessDataContext.SubmitChanges(); } } }
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
var originalEntity = dc.GetTable<T>().Where(x => x.id== entity.id).SingleOrDefault();
foreach (var property in newEntity.GetType().GetProperties())
{
PropertyInfo orignProperty = originalEntity.GetType().GetProperty(property.Name);
orignProperty.SetValue(originalEntity, property.GetValue(newEntity, null), null);
}
dc.SubmitChanges();
# re: LINQ to SQL and attaching Entities
public bool Update(tt_customer customer) { context = new TimeTrakkerContext(); //tt_customer Tcust = context.tt_customers.Single(c => c.Pk == customer.Pk); //create a new instance to avoid object tracking tt_customer Tcust = new tt_customer { Pk = customer.Pk }; context.tt_customers.Attach(customer, Tcust); context.SubmitChanges(); return true; }
# re: LINQ to SQL and attaching Entities
Within my middle tier function I have this code ...
using (var context = GetContext()) { Company company = new Company(companyId); context.Companies.Attach(company); company.UserName = userName; company.Password = password; company.EMail = email; company.Notes = notes; context.SubmitChanges(); }
First, the Attach should be called before you set any properties ... second if you set the PK property you get an exception so what you do is add a new contructor via partial class ...
partial class Company { public Company(int companyName) { _CompanyId = companyName; } }
This bypassed the PropertyChange ... it has worked out well for me ... however I also use VS2008 and latest .NET Framework
# re: LINQ to SQL and attaching Entities
It's interesting to see all the different approaches that have been mentioned in this thread. Obviously there are ways this can be done - iit'd just be nice if Microsoft would at least take one of these approaches and automate it behind the scenes.
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
Hello Everyone, I'm quite new to Linq and have had some problems updating my dabase. I don't know if it is actually happening for be using a global data context or I'm missing something. I have my typed DataContex and one Static Public Var to initialize it located within the namespace AlpaCommon, like following:
My partial datacontext***************
// partial datacontext classnamespaceAlpaCommon{public partial class AlpaDataContext : System.Data.Linq.DataContext{//Insert method is working...public void InsertAnimal2(Animal instance){Animais.InsertOnSubmit(instance);SubmitChanges();} //Delete method is working...public void DeleteAnimal2(int animalID){var animal = (from a in Animais where a.AnimalID == animalID select a).First();Animais.DeleteOnSubmit(animal);SubmitChanges();}//Update method IS NOT working...public void UpdateAnimal2(Animal newAnimal){.var animal = (from a in Animais where a.AnimalID == newAnimal.AnimalID select a).First();animal = newAnimal;SubmitChanges();}This is where I'm instanciating the datacontext e other variables I'll need in the whole app*********
//global DataContext instancenamespace AlpaCommon{public static class Globals{public static AlpaDataContext db = new AlpaDataContext();This is the call to the update method **************************
using AlpaCommon;namespace Animais{public partial class Altera : System.Web.UI.Page{protected void btnUpdate_Click(object sender, EventArgs e){try{//cria um novo Objeto do tipo AnimalAnimal animalAltera = new Animal();//set new values animalAltera.AnimalID = Convert.ToInt32(Request.Params["AnimalID"]);animalAltera.Castrado = CastradoCheckBox.Checked;animalAltera.DisponivelAdocao = DisponivelCheckBox.Checked;animalAltera.Adotado = AdotadoCheckBox.Checked;animalAltera.Nome = NomeTextBox.Text;animalAltera.Tipo = TipoDropDownList.SelectedValue;animalAltera.Sexo = SexoDropDownList.SelectedValue;animalAltera.Descricao = DescricaoTextBox.Text;animalAltera.Local = LocalTextBox.Text;animalAltera.Foto = AlteraFoto(); AlpaCommon.Globals.db.UpdateAnimal2(animalAltera);redirect = redirectSucesso;}catch{redirect = redirectErro;}finally{Helper.Redirect(redirect);}}
--------------------------------------------------------------------------------
I'm not catching any exception, it just does not update the database. Am I missing something in my updating or calling method? I'm looking forward for suggestions.
Thank you
Josimari Martarelli
# re: LINQ to SQL and attaching Entities
API like the one found in linq to sql has been around in java enterprise edition for years, so I eagerly waited for the first release of the dot net framework with linq bundled-in.
The exception telling you that your entity can't be attached because the key has been modified occurs because the key is the only means of reaching an entity. The key is the entity's identity so if you change it, it will seem as if it were another entity. Change whatever you like but keep the original key, then try to attach using the new values and the original values.
Aside from the technical stuff, if you really want to use linq in N-tier remote applications you have to get past the ADO paradigm and embrace the linq way of staring at the world.
Best regards.
Nicolas.
# re: LINQ to SQL and attaching Entities
but i try to look out and found this
http://www.codeproject.com/KB/linq/linqmultitier.aspx
Best regards
# re: LINQ to SQL and attaching Entities
I've written a simple article which explains how to properly reattach and submit a changed entity. It's rather easy once you know how to do it:
http://christophekeller.blogspot.com/2010/02/linq-to-sql-updating-reattached-entity.html
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
# Linq returns the whole table
I've been doing some maintenance work on a heavily Linq'd middle tier and was suprised to find that it seems to always retrieve the whole table via the Datacontext.GetTable<>().
Even with you line from above:
tt_customer Tcust = context.tt_customers.Single(c => c.Pk == customer.Pk);
The whole table will be pulled into the "repository".
Am I right? or have I been in the sun too long...
Underlying this is the real question: "how to get back just one record in the first place".
Regards
Rob
# re: LINQ to SQL and attaching Entities
private YourDataContext _context;
/// <summary>
/// Used to extend all entities in the datacontext
/// </summary>
public interface IBaseEntity
{
/// <summary>
/// Expected to be tables primary key
/// </summary>
int ID { get; set; }
}
/// <summary>
/// Saves an entity to the database.
/// </summary>
/// <returns>-1 = Failed. 0 = Update Ok, otherwise it returns the ID of the object</returns>
public int SaveEntity<TEntity>(TEntity entity) where TEntity : class, IBaseEntity
{
var original = _context.GetTable<TEntity>().FirstOrDefault(p => p.ID == entity.ID);
if (original != null)
{
return UpdateEntity(entity, original);
}
try
{
_context.GetTable<TEntity>().InsertOnSubmit(entity);
_context.SubmitChanges();
return entity.ID;
}
catch
{
return -1;
}
}
public int UpdateEntity<TEntity>(TEntity source, TEntity original) where TEntity : class, IBaseEntity
{
using (var saveContext = new YourDataContext(ConnectionString))
{
saveContext.GetTable<TEntity>().Attach(source, original);
try
{
saveContext.SubmitChanges();
return 0;
}
catch
{
return -1;
}
}
}
# re: LINQ to SQL and attaching Entities
// this context.GetTable<TType>().First(p=>p.ID == 1); // is the same as this context.TType.First(p=>p.ID == 1);
You can track what queries LTS generates like this:
public static void Main(string[] args)
{
using(var context = new DataContext())
{
// Sets the context to show every query done to the console window
context.Log = Console.Out;
// this wil generate a sql like: select top(1) from table where id = 1
context.GetTable<TEntity>().First(p => p.ID == 1);
// this wil generate a sql like: select * from table
// and then perform a normal linq query on the object collection.
context.GetTable<TEntity>().ToList().First(p => p.ID == 1);
}
}
# re: LINQ to SQL and attaching Entities
public bool UpdateMember(Member objMem)
{
DataClassesDataContext db = new DataClassesDataContext();
var mmbrs =
from p in db.Members
where p.Member_id == objMem.Member_id
select p;
foreach (var item in mmbrs)
{
item.Address = objMem.Address;
item.City = objMem.City;
item.FirstName = objMem.FirstName;
item.LastName = objMem.LastName;
}
//db.Members.Attach(objMem, true);//GetMemberById(objMem.Member_id)
db.SubmitChanges();
return true;
}
# re: LINQ to SQL and attaching Entities
# re: LINQ to SQL and attaching Entities
static void CopyProperties<T>(ref T Target, T Source)
{
foreach (PropertyInfo PI in Target.GetType().GetProperties())
{
if (PI.CanWrite && PI.CanRead)
{
PI.SetValue(Target, PI.GetValue(Source, null), null);
}
}
}
And while you will still end up with some extra traffic, it makes this sort of thing easy:
static void Save(Test_TableData ChangedData)
{
using (DataClasses1DataContext D = new DataClasses1DataContext())
{
Test_TableData UpdateTarget = D.Test_TableDatas.SingleOrDefault(i => i.ID == ChangedData.ID);
if (UpdateTarget != null)
{
CopyProperties<Test_TableData>(ref UpdateTarget, ChangedData);
}
else
{
D.Test_TableDatas.InsertOnSubmit(ChangedData);
}
D.SubmitChanges();
}
}
I am not working with incredibly complex SQL tables or anything, but for the time being, this gets the job done with relatively little code.
# re: LINQ to SQL and attaching Entities
Had exactly the same problem.
In my case, the problem was because I was using the same Context to perform a Query.
I mean, I looked for the Record if it didn't exists I did an InsertOnSubmit, if it did exists I did an Attach and there is where it failed (Did everything, rowversion column, tagged as timestamp in the .dbml etc)
When I decided to create to different instances of the Context, one for the search of the existence of the record, dispose that and then instantiate a new one for the Attach/Insert is just worked.
# re: LINQ to SQL and attaching Entities
Section 6.7 covers multi-tier entities and the attach syntax. One of my upcoming blog posts will cover this in more detail and cover scenarios were you take changes and apply them from a different tier.
Thanks,
Scott