7

在试图解决:

Linq .Contains with large set 导致 TDS 错误

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

(简短摘要)我想针对不是(完全或至少很容易)在 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

4 回答 4

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();
}
于 2010-09-16T21:30:11.500 回答
1

我们有类似的情况,虽然这可行,但问题在于您并没有真正处理 Queryables,因此您不能轻松地使用这个“with”LINQ。这不是一个适用于方法链的解决方案。

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

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

于 2010-10-05T17:20:21.750 回答
0

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

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

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

编辑:忘了它是一个临时表,临时表是每个连接的,所以我之前关于多线程的论点是不正确的。但是,对于强制执行固定模式,它仍然不是一个通用的解决方案。

于 2010-09-16T21:58:20.510 回答
0

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

即使它在那里,我认为这将是一个你假设查询和连接最终如何呈现的一些功能的领域,这就是 linq to sql 的一些大问题 - 你只是不知道他可能会发生什么跟踪工程师想出更好的做事方式。

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

于 2010-10-19T22:33:42.737 回答