tags:

views:

1471

answers:

6

Given two databases of identical schema, is there any easy way to transfer records between the two? I'm using LINQ to SQL as I need to perform a few additional operations along the way and having objects representing the records I'm transferring makes this much easier. This is a small-scale helper app, so SSIS is probably overkill and SQLBulkCopy doesn't allow me to easily interrogate objects en route.

What I'd like to do is this:

public static void TransferCustomer
                     (DBDataContext DCSource, 
                      DBDataContext DCDest, 
                      int CustomerID)
{
  Customer customer;
  using (DCSource = new DBDataContext(SourceConnStr))
  {
    customer = DCSource.Customers
                       .Where(c => c.CustomerID == CustomerID)
                       .Single();
  }

  using (DCDest = new DBDataContext(DestConnStr))
  {
    DCDest.Customers.InsertOnSubmit(customer);
    DCDest.SubmitChanges();
  }
}

But for obvious reasons, that throws an exception with the message:

An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.

I can get round this by doing a shallow copy of the object like this:

public static Customer Clone(this Customer customer)
{
  return new Customer()
  {
    Name = customer.Name,
    Email = customer.Email
    etc...
  };
}

and then inserting the cloned item. If I only copy the fields in question, and not any references representing table relationships this works fine. It's a bit long-winded however and the way I'm doing it needs me to manually assign every field in the Clone method. Could anyone suggest any ways of making this easier? The two things I'd like to know are:

  1. Can LINQ to SQL do this in an easier way,
  2. Is there a nice way of using Reflection to make a shallow copy which will insert?

Many thanks!

A: 

Is this purely an academic exercise?

If not i would seriously think about using SSIS: http://msdn.microsoft.com/en-us/library/ms141026.aspx instead, or failing that Rhino ETL: http://ayende.com/Blog/archive/2008/01/16/Rhino-ETL-2.0.aspxfor transfering data between databases.[link text][1]

Kev Hunter
This is real, but it's only a lightweight helper app for me to use. I don't anticipate transferring more than 50 rows across several tables in each batch, so I think SSIS is probably overkill. My current solution using a clone method per object works really nicely, but it feels inelegant. I need to add some more tables so I wanted to find a better way before I write more clone methods!
Jon Artus
+2  A: 

Here's a way to shallow clone LINQ objects (omitting the primary key): Copy Linq Objects.

It could be tweaked to use the meta-model if you aren't using attributes. Then all you'd need is 2 data-contexts (one source, one destination).

Alternatively, you could look at SqlBulkCopy - see Copy from one database table to another C#

Marc Gravell
That's really helpful thanks. I'm using auto-generated entity classes so they have all of the markup I need to use your clone method. It still feels like LINQ to SQL should make this easier, but if no-one knows how to do it, this will save me lots of time.
Jon Artus
A: 

Why use LINQ to do this? It seems a bit of waste for a framework to get in the way when you can make use of SQL Server's DTS.

uriDium
You don't even need DTS; SqlBulkCopy would work, and is about 5 lines of code.
Marc Gravell
I need to be able to perform a few extra operations along the way - in this case, having entity classes makes my life easier in other ways... I know this isn't the best way to transfer records but there are a few other factors which make me want to use LINQ.
Jon Artus
I think that you can do custom things with DTS. I am not too sure on how flexible it is. I think it is worth investigating if you don't find an answer here.
uriDium
@Marc, I am aware of SqlBulkCopy but would that be faster than A DTS or even a BCP out and BCP in again?
uriDium
A: 

I use the DataContractCloner to detach LINQ-to-SQL entities:

/// <summary>
/// Serializes the entity to XML.
/// </summary>
/// <returns>String containing serialized entity.</returns>
public string Serialize()
{
    return DataContractCloner.Serialize<Organization>(this);
}

/// <summary>
/// Deserializes the entity from XML.
/// </summary>
/// <param name="xml">Serialized entity.</param>
/// <returns>Deserialized entity.</returns>
public static Organization Deserialize(string xml)
{
    return DataContractCloner.Deserialize<Organization>(xml);
}

All LINQ-to-SQL entities are already annotated, so this creates an attachable clone that you can later re-attach with a few caveats. First, foreign key relationships will be resolved, so you must insert in order of dependency, and if you have circular relationships you'll have to remove the foreign keys from your schema and re-apply after the transfer. Second, LINQ-to-SQL does not perform an identity insert, so if you are using auto-increment identity columns you're going to have problems on submit. I haven't had success with forcing the DataContext to enable identity insert, but YMMV.

AndyM
Silly question maybe, but what's a DataContractCloner? I can't find any reference at all in the documentation...
Jon Artus
Yeah -- Google turns up exactly one result: this page!
Jim Raden
A: 

You might like to consider using the SQLBulkCopy class instead. You can find a code project article about it here: http://www.codeproject.com/KB/database/SqlBulkCopy.aspx. That would be a quicker way to do it, but I guess it will require you to create the queries yourself instead of letting LINQ-to-SQL do it.

Caleb Vear
A: 

Try this

....

using (DCDest = new DBDataContext(DestConnStr)) { DCDest.Customers.InsertOnSubmit(customer.ToList());

DCDest.SubmitChanges();

}