2009-06-17 6 views
7

Bei dem Versuch, zu lösen:Temporäre Tabellen in Linq - Jeder sieht ein Problem damit?

Linq .Contains with large set causes TDS error

Ich glaube, ich auf einer Lösung gestolpert, und ich würde gerne sehen, ob es das Problem der Annäherung an einem koscheren Weg.

(kurze Zusammenfassung) Ich möchte Linq-Join gegen eine Liste von Record-IDs, die nicht (vollständig oder zumindest leicht) in SQL generiert werden. Es ist eine große Liste und bläst häufig über die Grenze von 2100 Artikeln für den TDS-RPC-Aufruf. Was ich also in SQL getan hätte, würde sie in eine temporäre Tabelle werfen und dann gegen sie, wenn ich sie brauchte.

So machte ich das gleiche in Linq.

In meiner MyDB.dbml Datei, die ich hinzugefügt:

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

die Designer öffnen und schließen sie die erforderlichen Einträge dort hinzugefügt, obwohl für die Vollständigkeit, die ich von der MyDB.desginer.cs Datei zitieren werde:

[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)); 
        } 
      } 
    } 

Dann wurde es einfach eine Sache des Jonglierens um einige Dinge im Code. Wo würde ich normalerweise gehabt haben:

MyDBDataContext mydb = new MyDBDataContext(); 

ich es bekommen hatte seine Verbindung mit einem normalen SqlConnection zu teilen, so dass ich die Verbindung verwenden, könnte die temporäre Tabelle zu erstellen. Danach scheint es ziemlich brauchbar.

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(); 

und deren Verwendung:

// 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; 

Sieht jemand ein Problem mit diesem Ansatz als Allzwecklösung für die Verwendung von temporären Tabellen in in Linq verbindet?

Es löste mein Problem wunderbar, da ich jetzt eine direkte Verbindung in Linq machen kann, anstatt .Contains() zu verwenden.

Postscript: Das einzige Problem, das ich habe, ist, dass auf dem Tisch Linq und regelmäßige SqlCommands Mischen (wo man Lesen/Schreiben und so das andere ist) kann gefährlich sein. Immer mit SqlCommands in die Tabelle einfügen, und dann Linq-Befehle zum Lesen es funktioniert gut. Anscheinend speichert Linq Ergebnisse - es gibt wahrscheinlich einen Weg, aber es war nicht offensichtlich.

Antwort

3

Ich sehe kein Problem mit der Verwendung von temporären Tabellen, um Ihr Problem zu lösen. Was das Mischen von SqlCommands und LINQ angeht, sind Sie bezüglich des Gefahrenfaktors absolut korrekt. Es ist so einfach, Ihre SQL-Anweisungen ausführen, um eine Datacontext verwenden, würde ich nicht einmal über die SqlCommand Sorge:

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(); 
} 
0

Als „Allzwecklösung“, was, wenn Sie Code in mehreren Threads ausgeführt wird/Apps? Ich denke, große Lösung ist immer auf die Problemdomäne bezogen. Es ist besser, eine normale Tabelle für das Problem zu verwenden, an dem Sie gerade arbeiten.

Ich habe einmal eine "generische" Listentabelle in der Datenbank erstellt. Die Tabelle wurde mit drei Spalten erstellt: int, uniqueidentifier und varchar sowie weitere Spalten zum Verwalten der einzelnen Listen. Ich dachte: "Es sollte reichen, um viele Fälle zu behandeln". Aber bald habe ich eine Aufgabe erhalten, die eine Verknüpfung mit einer Liste von drei ganzen Zahlen erfordert.Danach habe ich nie wieder versucht, eine "generische" Listentabelle zu erstellen.

Außerdem ist es besser, einen SP zu erstellen, um bei jedem Datenbankaufruf mehrere Elemente in die Listentabelle einzufügen. Sie können ~ 2000 Artikel in weniger als 2 db Rundreisen einfach einfügen. Abhängig davon, was Sie tun, spielt die Leistung keine Rolle.

EDIT: vergessen, es ist eine temporäre Tabelle und temporäre Tabelle ist pro Verbindung, so dass mein vorheriges Argument auf Multithreads nicht korrekt war. Dennoch ist es keine allgemeine Lösung, um das festgelegte Schema durchzusetzen.

1

Wir haben eine ähnliche Situation, und während dies funktioniert, wird das Problem, dass Sie nicht wirklich mit Queryables beschäftigen, so dass Sie nicht einfach mit "LINQ" verwenden können. Dies ist keine Lösung, die mit Methodenketten arbeitet.

Unsere endgültige Lösung war nur zu werfen, was wir in einer gespeicherten Prozedur wollen, und schreiben wählt in dieser Prozedur gegen die temporäre Tabellen, wenn wir diese Werte wollen. Es ist ein Kompromiss, aber beide sind Workarounds. Zumindest mit dem gespeicherten Prozess wird der Designer den aufrufenden Code für Sie generieren, und Sie haben eine Black-Box-Implementierung. Wenn Sie also weiter optimieren müssen, können Sie dies innerhalb der Prozedur ohne Neukompilierung tun.

In einer perfekten Welt gibt es einige zukünftige Unterstützung für das Schreiben von Linq2Sql-Anweisungen, die Sie die Verwendung von temporären Tabellen in Ihren Abfragen angeben können, vermeiden die unangenehme SQL IN-Anweisung für komplexe Szenarien wie diese.

0

Würde die von Neil angebotene Lösung tatsächlich funktionieren? Wenn es sich um eine temporäre Tabelle handelt und jede der Methoden ihren eigenen Datenkontext erstellt und entsorgt, glaube ich nicht, dass die temporäre Tabelle nach dem Löschen der Verbindung noch vorhanden wäre.

Auch wenn es dort war, denke ich, dass dies ein Bereich wäre, in dem Sie einige Funktionen annehmen, wie Abfragen und Verbindungen gerendert werden, und das ist eines der großen Probleme mit linq to sql - Sie wissen einfach nicht was Es könnte passieren, dass er nachdenkt, während die Ingenieure bessere Methoden entwickeln.

Ich würde es in einem gespeicherten Prozess tun. Sie können die Ergebnismenge immer in eine vordefinierte Tabelle zurückgeben, wenn Sie möchten.

+0

Um ehrlich zu sein, habe ich nicht die Lösung getestet, die ich mit temporären Tabellen bereitgestellt habe. Davon abgesehen wird die Lösung definitiv mit "permanenten" Tabellen funktionieren. Der Grund, warum ich die DataContext.ExecuteCommand() -Methode verwende, liegt auch daran, dass die SQL-Anweisung von der LINQ-Engine überhaupt nicht verarbeitet wird. Was Sie senden, ist, was ausgeführt wird. –