代码之家  ›  专栏  ›  技术社区  ›  Clinton Pierce

Linq中的临时表-有人看到这个问题吗?

  •  7
  • Clinton Pierce  · 技术社区  · 16 年前

    试图解决:

    Linq .Contains with large set causes TDS error

    我想我偶然发现了一个解决方案,我想看看这是否是一种解决问题的犹太方式。

    (简短的摘要)我想根据一个记录ID列表进行LINQ联接,该记录ID在SQL中不是(完全或至少很容易)生成的。这是一个大列表,经常超过2100项的TDS RPC调用限制。所以我在SQL中所做的是将它们放入临时表中,然后在需要时加入到临时表中。

    所以我在林肯也这么做了。

    在mydb.dbml文件中,我添加了:

    <Table Name="#temptab" Member="TempTabs">
      <Type Name="TempTab">
        <Column Name="recno" Type="System.Int32" DbType="Int NOT NULL" 
              IsPrimaryKey="true" CanBeNull="false" />
      </Type>
    </Table>
    

    打开设计器并关闭它会在其中添加必要的条目,尽管为了完整性,我将引用mydb.desenginer.cs文件:

       [Table(Name="#temptab")]
       public partial class TempTab : INotifyPropertyChanging, INotifyPropertyChanged
       {
    
               private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    
               private int _recno;
    
    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnrecnoChanging(int value);
    partial void OnrecnoChanged();
    #endregion
    
               public TempTab()
               {
                       OnCreated();
               }
    
               [Column(Storage="_recno", DbType="Int NOT NULL", IsPrimaryKey=true)]
               public int recno
               {
                       get
                       {
                               return this._recno;
                       }
                       set
                       {
                               if ((this._recno != value))
                               {
                                       this.OnrecnoChanging(value);
                                       this.SendPropertyChanging();
                                       this._recno = value;
                                       this.SendPropertyChanged("recno");
                                       this.OnrecnoChanged();
                               }
                       }
               }
    
               public event PropertyChangingEventHandler PropertyChanging;
    
               public event PropertyChangedEventHandler PropertyChanged;
    
               protected virtual void SendPropertyChanging()
               {
                       if ((this.PropertyChanging != null))
                       {
                               this.PropertyChanging(this, emptyChangingEventArgs);
                       }
               }
    
               protected virtual void SendPropertyChanged(String propertyName)
               {
                       if ((this.PropertyChanged != null))
                       {
                               this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                       }
                }
        }
    

    然后它就变成了一个在代码中处理一些事情的问题。在我通常会有的地方:

    MyDBDataContext mydb = new MyDBDataContext();
    

    我必须让它与一个正常的sqlconnection共享它的连接,这样我就可以使用连接来创建临时表。之后,它看起来很有用。

    string connstring = "Data Source.... etc..";
    SqlConnection conn = new SqlConnection(connstring);
    conn.Open();
    
    SqlCommand cmd = new SqlCommand("create table #temptab " +
                                    "(recno int primary key not null)", conn);
    cmd.ExecuteNonQuery();
    
    MyDBDataContext mydb = new MyDBDataContext(conn);
    // Now insert some records (1 shown for example)
    TempTab tt = new TempTab();
    tt.recno = 1;
    mydb.TempTabs.InsertOnSubmit(tt);
    mydb.SubmitChanges();
    

    并使用它:

    // Through normal SqlCommands, etc...
    cmd = new SqlCommand("select top 1 * from #temptab", conn);
    Object o = cmd.ExecuteScalar();
    
    // Or through Linq
    var t = from tx in mydb.TempTabs
            from v in  mydb.v_BigTables
            where tx.recno == v.recno
            select tx;
    

    是否有人将这种方法的问题看作是在LINQ的联接中使用临时表的通用解决方案?

    它很好地解决了我的问题,因为现在我可以在linq中直接进行连接,而不必使用.contains()。

    后记 : 我遇到的一个问题是,在表上混合使用linq和常规sql命令(其中一个命令在读/写,另一个命令在读/写)可能很危险。总是使用sqlcommands在表中插入数据,然后使用linq命令读取数据就可以了。显然,Linq缓存结果——可能有一种方法可以绕过它,但它不是显而易见的。

    4 回复  |  直到 14 年前
        1
  •  3
  •   Neil T.    14 年前

    我认为使用临时表解决您的问题没有问题。至于混合使用sqlcommands和linq,您对危险因素是绝对正确的。使用DataContext执行SQL语句非常简单,我甚至不担心sqlcommand:

    private string _ConnectionString = "<your connection string>";
    
    public void CreateTempTable()
    {
        using (MyDBDataContext dc = new MyDBDataContext(_ConnectionString))
        {
            dc.ExecuteCommand("create table #temptab (recno int primary key not null)");
        }
    }
    
    public void DropTempTable()
    {
        using (MyDBDataContext dc = new MyDBDataContext(_ConnectionString))
        {
            dc.ExecuteCommand("DROP TABLE #TEMPTAB");
        }
    }
    
    public void YourMethod()
    {
        CreateTempTable();
    
        using (MyDBDataContext dc = new MyDBDataContext(_ConnectionString))
        {
            ...
            ... do whatever you want (within reason)
            ...
        }
    
        DropTempTable();
    }
    
        2
  •  1
  •   bopapa_1979    14 年前

    我们有一个类似的情况,虽然这是可行的,但问题是您并没有真正处理可查询的,所以您不能轻易地使用这个“with”linq。这不是适用于方法链的解决方案。

    我们的最终解决方案只是在存储过程中抛出所需的内容,并在需要这些值时在该过程中针对临时表写入所选内容。这是一种妥协,但两者都是解决办法。至少在存储过程中,设计器将为您生成调用代码,并且您有一个黑盒实现,因此如果需要进一步优化,可以在过程中严格执行,而无需重新编译。

    在一个完美的世界中,将来会有一些对编写linq2sql语句的支持,这些语句允许您指示在查询中使用临时表,避免在这种复杂场景中使用讨厌的sql-in语句。

        3
  •  0
  •   Codism    14 年前

    作为“通用解决方案”,如果代码在多个线程/应用程序中运行,会怎么样?我认为大列表解决方案总是与问题域相关。对于您正在处理的问题,最好使用常规表。

    我曾经在数据库中创建了一个“通用”列表表。该表由三列创建:int、uniqueidentifier和varchar,以及其他用于管理每个列表的列。我在想:“这应该足以应付许多案件。”但很快我就收到一个任务,它要求用三个整数的列表来执行一个连接。此后,我再也没有尝试创建“通用”列表表。

    另外,最好创建一个SP,以便在每次数据库调用中向列表表中插入多个项。您可以在少于2 db的往返行程中轻松插入~2000个项目。当然,取决于你在做什么,性能可能并不重要。

    编辑:忘记了它是一个临时表,临时表是每个连接的,所以我以前在多线程上的参数不正确。但是,它并不是强制执行固定模式的通用解决方案。

        4
  •  0
  •   Tom    14 年前

    尼尔提供的解决方案真的有效吗?如果它是一个临时表,并且每个方法都在创建和处理自己的数据上下文,那么我认为在断开连接后临时表不会仍然存在。

    即使它在那里,我认为这将是一个你假设查询和连接是如何被呈现的一些功能的领域,这是Linq to SQL的一个大问题-你只是不知道在工程师们想出更好的方法做事情的时候,可能会发生什么。

    我会在存储过程中完成。如果愿意,您可以将结果集返回到预定义的表中。