将LINQ to SQL表映射到另一个非常相似的表而不迭代每个表

时间:2010-04-08 21:47:14

标签: c# linq

对模糊问题道歉。这是:我有一个由LINQ to SQL创建的表对象。我正在将条目从一个表复制到一个“存档”表,其中包含原始列的所有列,以及一些额外的列。

迭代遍历每个对象并手动定义映射感觉很草率:

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();

有一种聪明的方法吗?

2 个答案:

答案 0 :(得分:2)

考虑使用dbConext.ExecuteCommand()函数直接执行SQL,例如:

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

或者你可以使用InsertAllOnSubmit,例如:

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

修改2010-04-09:

IQueryable传递到InsertAllOnSubmit构建新项目(即new NewTable())失败的原因如下(source):

  

此检查已添加,因为它应该从一开始就存在并且丢失了。手动构建实体实例作为投影会使用可能格式错误的对象污染缓存,从而导致程序员和我们的许多错误报告混乱。此外,预测实体是应该在缓存中还是根本不进行跟踪是不明确的。实体的使用模式是它们是在查询之外创建的,并通过DataContext插入表中,然后通过查询检索,而不是由查询创建。

因此,错误发生是因为IQueryable正在尝试通过执行返回select指定的项目的SQL查询来在缓存上创建项目。使用IQueryable函数将IEnumberable转换为AsEnumerable()会破坏SQL生成。因此生成的查询只选择项目(即SQL不执行映射),并且新项目的构造在Linq to SQL逻辑之外完成。

为了确保我使用Northwind DB测试了该方法,我在其中使用以下代码创建了Categories表的副本:

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
}

答案 1 :(得分:1)

您可以使用Automapper(http://automapper.codeplex.com/)或一些非常简单的反射代码将数据从一个对象复制到另一个对象,这样您就不需要手动写出每个字段。

例如,使用界面确保它们匹配: -

   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();

注意:这不会处理数组,您可以扩展它来执行此操作,或者您可以只使用自动映射。