Linq的临时表 - 任何人都看到这个问题?

时间:2009-06-17 19:13:59

标签: linq tsql temp-tables

试图解决:

Linq .Contains with large set causes TDS error

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

(简短摘要)我想对一个没有(完全或至少很容易)在SQL中生成的记录ID列表进行linq-join。这是一个很大的列表,经常超过TDS RPC调用的2100项限制。所以我在SQL中所做的就是把它们放在一个临时表中,然后在我需要的时候加入它。

所以我在Linq做了同样的事。

在我的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.desginer.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和常规SqlCommands(其中一个是读/写,另一个是另一个)可能是危险的。总是使用SqlCommands在表上插入,然后Linq命令读取它工作正常。显然,Linq缓存结果 - 可能有一种解决方法,但这并不明显。

4 个答案:

答案 0 :(得分:3)

我没有看到使用临时表来解决问题的问题。至于混合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();
}

答案 1 :(得分:1)

我们有类似的情况,虽然这有效,但问题在于你并没有真正处理Queryables,所以你不能轻易地使用这个“with”LINQ。这不是适用于方法链的解决方案。

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

在一个完美的世界中,将来会有一些编写Linq2Sql语句的支持,这些语句允许您在查询中指定临时表的使用,避免在这样的复杂场景中使用令人讨厌的sql IN语句。

答案 2 :(得分:0)

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

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

此外,最好创建一个SP,以便在每个数据库调用中将多个项插入到列表中。您可以在不到2分钟的往返中轻松插入~2000个项目。原因,取决于你在做什么,表现可能无关紧要。

编辑:忘记它是一个临时表,临时表是每个连接,所以我之前关于多线程的论点是不正确的。但是,实施固定架构仍然不是一般解决方案。

答案 3 :(得分:0)

Neil提供的解决方案是否真的有效?如果它是一个临时表,并且每个方法都在创建和处理它自己的数据上下文,我不认为临时表在连接被删除后仍然存在。

即使它存在,我认为这将是一个你正在假设查询和连接如何最终呈现的功能的区域,以及linq到sql的大问题 - 你只是不知道什么可能当工程师提出更好的做事方式时,他会跟踪他的追踪。

我会在存储过程中执行此操作。如果愿意,您始终可以将结果集返回到预定义的表中。