代码之家  ›  专栏  ›  技术社区  ›  Daniel Coffman

将LINQ到SQL表映射到另一个非常相似的表,而不必遍历每个表

  •  2
  • Daniel Coffman  · 技术社区  · 15 年前

    为这个模糊的问题道歉。这里是:我有一个由linq to sql创建的表对象。我正在将条目从一个表复制到一个“archive”表中,该表包含原始表的所有列,外加一些额外的列。

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

    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 回复  |  直到 15 年前
        1
  •  2
  •   AxelEckenberger    15 年前

    考虑使用 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插入表中,然后通过查询检索,而不是通过查询创建。

    所以错误发生的原因是 可查询的 正在尝试通过执行返回select指定的项的SQL查询在缓存上创建项。转换 可查询的 变成一个 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
    }
    
        2
  •  1
  •   Ian Mercer    15 年前

    你可以用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();
    

    注意:这不处理数组,您可以扩展它来实现这一点,或者您可以只使用automapper。