tags:

views:

220

answers:

3

Apologies for the vague question. Here it is: I have a table object created by LINQ to SQL. I am copying entries from one table to an "archive" table that has all the columns of the original, plus a couple extra ones.

It feels sloppy to iterate through every object and manually define the mapping like:

foreach (oldTable in dbContextOldTables)
{
   var newTable = new NewTable();
   newTable.TableID = oldTable.ID;
   newTable.Title = oldTable.Title;
   ... (repeat for twenty columns)
   newTable.ColumnThatIsntInOldTable = "Data that will be the same for every row";
   dbContext.NewTables.InsertOnSubmit(newTable);
} 
dbContext.SubmitChanges();

Is there a clever way to do this?

+2  A: 

Consider using dbConext.ExecuteCommand() function to execute SQL directly, e.g.:

ctx.ExecuteCommand("INSERT INTO backup SELECT * FROM original");

Alternatively you could use InsertAllOnSubmit, e.g.:

var entries = dbContextOldTables.AsEnumerable().Select(x => new NewTable() { /* mapping */ });
dbContext.NewTables.InsertAllOnSubmit(entries);

Edit 2010-04-09:

Passing an IQueryable into InsertAllOnSubmit that constructs a new item (i.e. new NewTable()) fails for the following reason (source):

This check was added because it was supposed to be there from the beginning and was missing. Constructing entity instances manually as a projection pollutes the cache with potentially malformed objects, leading to confused programmers and lots of bug reports for us. In addition, it is ambiguous whether projected entities should be in the cache or changed tracked at all. The usage pattern for entities is that they are created outside of queries and inserted into tables via the DataContext and then later retrieved via queries, never created by queries.

So the error occurs because the IQueryable is trying to create an item on the cache by executing and SQL query that returns the item specified by the select. Converting the IQueryable into an IEnumberable using the AsEnumerable() function breaks the SQL generation. So the query generated just selects the item (i.e. the SQL does not do the mapping) and the construction of the new item is done outside the Linq to SQL logic.

To be sure I tested the approach using a Northwind DB in which I created a copy of the Categories table using the code below:

using (var ctx = new NorthwindDataContext()) {
    var categories = ctx.Categories;

    var catcopy = categories.Select(x => new CategoriesBackup() {
        CategoryID = x.CategoryID,
        CategoryName = x.CategoryName,
        Description = x.Description,
        Picture = x.Picture
    });
    //ctx.CategoriesBackups.InsertAllOnSubmit(catcopy);  // THIS DOES NOT WORK

    var catcopy2 = categories.AsEnumerable().Select(x => new CategoriesBackup() {
        CategoryID = x.CategoryID,
        CategoryName = x.CategoryName,
        Description = x.Description,
        Picture = x.Picture
    });
    ctx.CategoriesBackups.InsertAllOnSubmit(catcopy2);  // THIS WORKS
}
Obalix
Yes, it would probably be easier to just write a stored procedure to do this.
Daniel Coffman
Look on the alternative ... edited after the comment.
Obalix
You can't create entities inside a query in this way.
Daniel Coffman
@Danial Coffman: What error do you get ... normally it should work, you have to do the mapping between the old and the new tables where I have put `/* mapping */`. I.e put `newTable.TableID = oldTable.ID; newTable.Title = oldTable.Title; ... (repeat for twenty columns) newTable.ColumnThatIsntInOldTable = "Data that will be the same for every row";` here.
Obalix
@Danial Coffman: Edited the answer (added .AsEnumerable() to the select query), I think this should solve the problem.
Obalix
Exception Details: System.NotSupportedException: Explicit construction of entity type 'NewTable' in query is not allowed -- creation of entities inside LINQ queries like this is explicitly disallowed, but you can easily bypass this by instead passing a delegate into the .Select() that does the construction for you. Don't see why AsEnumerable would make any difference here.
Daniel Coffman
@Danial Coffman: AsEnumerable() does the trick, see edit for the reason, and also for the reason why the error occurs.
Obalix
A: 

Why not something like(not tested or compiled):

dbContext.InsertAllOnSubmit(dbContextOldTables.Select(old=> new NewTable(){TableID = old.ID, Title = old.Title /*etc etc*/}));
dbContext.SubmitChanges(); 
Oskar Kjellin
Actually, this approach is not allowed and throws exception: Explicit construction of entity type 'NewTable' in query is not allowed.
Daniel Coffman
You can actually circumvent this by passing an anonymous delegate that does the mapping for you into the Select though.
Daniel Coffman
A: 

You could use Automapper (http://automapper.codeplex.com/) or some very simple reflection code to copy the data from one object to the other so you don't need to manually write out each field.

For example, using an interface to ensure they match:-

   public interface IShared
    {
        int Prop1 {get; set;}
        string Prop2 {get; set;}
    }

    public class A : IShared
    {
        public int Prop1 {get; set;}
        public string Prop2 {get; set;}
    }

    public class B : IShared
    {
        public int Prop1 {get; set;}
        public string Prop2 {get; set;}
    }


    static void Main(string[] args)
    {
        A A = new A(){ Prop1 = 1, Prop2 = "2" };
        B B = new B();

        var properties = typeof(IShared).GetProperties();
        foreach (var prop in properties)
        {
            object currentValue = prop.GetValue(A, null);
            prop.SetValue(B, currentValue, null);
        }

        Console.WriteLine("B = " + B.Prop1 + " " + B.Prop2);
        Console.ReadKey();

Note: This does NOT handle arrays, you could extend it to do that, or your could just use Automapper.

Hightechrider
Yes, automapper would probably work here because the naming structure of the entities is the same (for the shared fields). Could you give me more information on how I would use reflection to avoid manually defining a mapping? We've already solved the iteration problem, but mapping each field still feels a little burdensome.
Daniel Coffman
Example of using reflection to copy values added
Hightechrider
you don't need reflection, casting to IShared is enough
Catalin DICU
@Catalin - how do you propose creating an object of type B using just casting??
Hightechrider