Linq 中的临时表——有人发现这个问题吗?
在尝试解决:
Linq .Contains with large set Causes TDS error
我想我偶然发现了一个解决方案,并且我会想看看这是否是解决问题的正确方法。
(简短的总结)我想对未(完全或至少容易)在 SQL 中生成的记录 ID 列表进行 linq-join。 这是一个很大的列表,并且经常超出 TDS RPC 调用的 2100 项限制。 因此,我在 SQL 中所做的就是将它们放入临时表中,然后在需要时加入该表。
所以我在 Linq 中做了同样的事情。
我添加了:
<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));
}
}
}
在我的 MyDB.dbml 文件中, 代码。 我通常会遇到的情况是:
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 和常规 SqlCommand(一个在读/写,另一个也在读/写)可能会很危险。 始终使用 SqlCommands 在表上插入,然后使用 Linq 命令读取它效果很好。 显然,Linq 缓存结果——可能有一种解决方法,但并不明显。
In trying to solve:
Linq .Contains with large set causes TDS error
I think I've stumbled across a solution, and I'd like to see if it's a kosher way of approaching the problem.
(short summary) I'd like to linq-join against a list of record id's that aren't (wholly or at least easily) generated in SQL. It's a big list and frequently blows past the 2100 item limit for the TDS RPC call. So what I'd have done in SQL is thrown them in a temp table, and then joined against that when I needed them.
So I did the same in Linq.
In my MyDB.dbml file I added:
<Table Name="#temptab" Member="TempTabs">
<Type Name="TempTab">
<Column Name="recno" Type="System.Int32" DbType="Int NOT NULL"
IsPrimaryKey="true" CanBeNull="false" />
</Type>
</Table>
Opening the designer and closing it added the necessary entries there, although for completeness, I will quote from the MyDB.desginer.cs file:
[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));
}
}
}
Then it simply became a matter of juggling around some things in the code. Where I'd normally have had:
MyDBDataContext mydb = new MyDBDataContext();
I had to get it to share its connection with a normal SqlConnection so that I could use the connection to create the temporary table. After that it seems quite usable.
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();
And using it:
// 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;
Does anyone see a problem with this approach as a general-purpose solution for using temporary tables in joins in Linq?
It solved my problem wonderfully, as now I can do a straightforward join in Linq instead of having to use .Contains().
Postscript:
The one problem I do have is that mixing Linq and regular SqlCommands on the table (where one is reading/writing and so is the other) can be hazardous. Always using SqlCommands to insert on the table, and then Linq commands to read it works out fine. Apparently, Linq caches results -- there's probably a way around it, but it wasn't obviousl.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为使用临时表来解决您的问题没有问题。 至于混合 SqlCommands 和 LINQ,您对危险因素的看法是完全正确的。 使用 DataContext 执行 SQL 语句非常容易,我什至不用担心 SqlCommand:
I don't see a problem with using temporary tables to solve your problem. As far as mixing SqlCommands and LINQ, you are absolutely correct about the hazard factor. It's so easy to execute your SQL statements using a DataContext, I wouldn't even worry about the SqlCommand:
我们也有类似的情况,虽然这有效,但问题是您并没有真正处理 Queryables,因此您无法轻松地“使用”LINQ。 这不是一个适用于方法链的解决方案。
我们的最终解决方案只是将我们想要的内容放入存储过程中,并在需要这些值时在该过程中针对临时表写入选择。 这是一种妥协,但两者都是解决方法。 至少使用存储过程,设计器将为您生成调用代码,并且您有一个黑盒实现,因此如果您需要进行进一步的调整,您可以严格在过程中进行,而无需重新编译。
在完美的世界中,未来将会支持编写 Linq2Sql 语句,允许您在查询中指示临时表的使用,避免在像这样的复杂场景中使用令人讨厌的 sql IN 语句。
We have a similar situation, and while this works, the issue becomes that you aren't really dealing with Queryables, so you cannot easily use this "with" LINQ. This isn't a solution that works with method chains.
Our final solution was just to throw what we want in a stored procedure, and write selects in that procedure against the temp tables when we want those values. It is a compromise, but both are workarounds. At least with the stored proc the designer will generate the calling code for you, and you have a black boxed implementation so if you need to do further tuning you can do so strictly within the procedure, without a recompile.
In a perfect world, there will be some future support for writing Linq2Sql statements that allow you to dicate the use of temp tables within your queries, avoid the nasty sql IN statement for complex scenarios like this one.
作为一种“通用解决方案”,如果您的代码在多个线程/应用程序中运行怎么办? 我认为大列表解决方案总是与问题领域相关。 最好使用常规表格来解决您正在处理的问题。
我曾经在数据库中创建了一个“通用”列表。 该表由三列创建:int、uniqueidentifier 和 varchar,以及用于管理每个列表的其他列。 我想:“应该足以处理很多情况了”。 但很快我就收到了一项任务,要求对三个整数的列表执行联接。 之后,我再也没有尝试过创建“通用”列表。
另外,最好创建一个 SP,以便在每次数据库调用时将多个项目插入列表中。 您可以在不到 2 db 的往返时间内轻松插入约 2000 个项目。 当然,根据您在做什么,性能可能并不重要。
编辑:忘记它是一个临时表,临时表是每个连接的,所以我之前关于多线程的论点是不正确的。 但它仍然不是强制执行固定模式的通用解决方案。
As a "general-purpose solution", what if you code is run in more than one threads/apps? I think big-list solution is always related to the problem domain. It's better to use a regular table for the problem you are working on.
I once created a "generic" list table in database. The table was created with three columns: int, uniqueidentifier and varchar, along with other columns to manage each list. I was thinking: "it ought to be enough to handle many cases". But soon I received a task that requires a join be performed with a list on three integers. After that, I never tried to create "generic" list table again.
Also, it's better to create a SP to insert multiple items into the list table in each database call. You can easily insert ~2000 items in less than 2 db round trips. Of cause, depending on what you are doing, performance may do not matter.
EDIT: forgot it is a temporary table and temporary table is per connection, so my previous argument on multi-threads was not proper. But still, it is not a general solution, for enforcing the fixed schema.
尼尔提供的解决方案真的有效吗? 如果它是一个临时表,并且每个方法都在创建和处理自己的数据上下文,那么我认为在连接断开后临时表不会仍然存在。
即使它在那里,我认为这将是一个你假设查询和连接最终如何呈现的一些功能的区域,这就是 linq to sql 的一些大问题 - 你只是不知道他下面可能会发生什么跟踪工程师提出更好的做事方法。
我会在存储过程中完成它。 如果您愿意,您始终可以将结果集返回到预定义的表中。
Would the solution offered by Neil actually work? If its a temporary table, and each of the methods is creating and disposing its own data context, I dont think the temporary table would still be there after the connection was dropped.
Even if it was there, I think this would be an area where you are assuming some functionality of how queries and connections end up being rendered, and thats ome of the big issues with linq to sql - you just dont know what might happen downt he track as the engineers come up with better ways of doing things.
I'd do it in a stored proc. You can always return the result set into a pre-defined table if you wish.