2009-06-17 22 views
7

Próbując rozwiązać:Tabele tymczasowe w Linq - Czy ktoś ma z tym problem?

Linq .Contains with large set causes TDS error

myślę, że natknął się na rozwiązanie, i chciałbym, aby zobaczyć, czy jest to koszerny sposób zbliża się do problemu.

(krótkie podsumowanie) Chciałbym dołączyć do linq-join z listą identyfikatorów rekordów, które nie są (w całości lub przynajmniej łatwo) generowane w SQL. Jest to duża lista i często przekracza limit 2100 pozycji dla wywołania RPC TDS. Więc to, co bym zrobił w SQL, rzuciłoby je w tymczasowy stół, a potem połączyłem się z nim, gdy ich potrzebowałem.

Zrobiłem to samo w Linq.

W moim pliku MyDB.dbml I dodaje:

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

Otwieranie i zamykanie projektant dodał niezbędne wpisy tam, chociaż pod względem kompletności, będę cytować z pliku 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)); 
        } 
      } 
    } 

Potem stało się po prostu żonglowanie niektórymi rzeczami w kodzie. Gdzie bym normalnie mieli:

MyDBDataContext mydb = new MyDBDataContext(); 

musiałem zmusić go do udostępniania połączenia z normalnym SqlConnection, abym mógł korzystać z połączenia, aby utworzyć tabelę tymczasową. Po tym wydaje się całkiem użyteczny.

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

i używając go:

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

Czy ktoś widzi problem z tym podejściem, jako rozwiązanie dla ogólnego przeznaczenia przy użyciu tabel tymczasowych w przyłącza się Linq?

To rozwiązało mój problem cudownie, ponieważ teraz mogę zrobić proste dołączenie do Linq zamiast używać .Contains().

Postscript: Jeden problem mam jest to, że mieszanie Linq i regularne SqlCommands na stole (gdzie jedna jest odczytu/zapisu i tak jest inny) może być niebezpieczne. Zawsze używa SqlCommands do wstawienia do tabeli, a następnie polecenia Linq do odczytu działają dobrze. Wygląda na to, że Linq buforuje wyniki - prawdopodobnie jest to możliwe, ale nie było to oczywiste.

Odpowiedz

3

Nie widzę problemu z używaniem tabel tymczasowych do rozwiązania problemu. Jeśli chodzi o mieszanie SqlCommands i LINQ, masz całkowitą rację co do współczynnika zagrożenia. To jest tak łatwe do wykonania swoich SQL przy użyciu DataContext, nie będę nawet martwić o 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(); 
} 
0

jako „rozwiązania ogólnego przeznaczenia”, co, jeśli kod jest uruchamiany w więcej niż jednej nici/aplikacje? Myślę, że rozwiązanie z dużą listą jest zawsze związane z domeną problemu. Lepiej używać zwykłego stołu dla problemu, nad którym pracujesz.

Kiedyś utworzyłem "ogólną" listę w bazie danych. Tabela została utworzona z trzema kolumnami: int, uniqueidentyfikator i varchar oraz innymi kolumnami do zarządzania każdą listą. Myślałem: "to powinno wystarczyć, aby poradzić sobie z wieloma przypadkami". Ale wkrótce otrzymałem zadanie, które wymaga wykonania sprzężenia z listą na trzech liczbach całkowitych.Potem nigdy nie próbowałem ponownie utworzyć "ogólnej" listy list.

Ponadto lepiej jest utworzyć SP, aby wstawić wiele pozycji do tabeli list w każdym wywołaniu bazy danych. Możesz łatwo wstawić ~ 2000 przedmiotów w mniej niż 2 dB podróży w obie strony. Przyczyna, w zależności od tego, co robisz, wydajność może nie mieć znaczenia.

EDYCJA: zapomniałem, że jest to tabela tymczasowa, a tabela tymczasowa jest za połączenie, więc mój poprzedni argument dotyczący wielu wątków nie był prawidłowy. Ale nadal nie jest to ogólne rozwiązanie do egzekwowania stałego schematu.

1

Mamy podobną sytuację i chociaż działa to, problem polega na tym, że tak naprawdę nie mamy do czynienia z Queryables, więc nie można łatwo użyć tego "z" LINQ. To nie jest rozwiązanie, które działa z łańcuchami metod.

Naszym ostatecznym rozwiązaniem było po prostu wyrzucenie tego, co chcemy w procedurze przechowywanej, i zapisywanie zaznaczeń w tej procedurze w tabelach tymczasowych, gdy chcemy te wartości. To kompromis, ale oba są rozwiązaniem. Przynajmniej z zapisanym procem projektant wygeneruje kod wywołujący dla ciebie, a będziesz miał implementację z czarną skrzynką, więc jeśli musisz zrobić dalsze dostrajanie, możesz to zrobić ściśle w ramach procedury, bez ponownej kompilacji.

W idealnym świecie, będzie pewne przyszłe wsparcie dla pisania instrukcji Linq2Sql, które pozwolą Ci na użycie tabel tymczasowych w swoich zapytaniach, unikniesz nieprzyjemnej instrukcji SQL IN dla złożonych scenariuszy, takich jak ten.

0

Czy rozwiązanie oferowane przez Neila rzeczywiście działa? Jeśli jest to tabela tymczasowa, a każda z metod tworzy i pozbywa się własnego kontekstu danych, nie sądzę, aby tabela tymczasowa nadal istniała po zerwaniu połączenia.

Nawet jeśli był tam, myślę, że byłby to obszar, w którym zakładasz jakąś funkcjonalność w jaki sposób kwerendy i połączenia kończą się renderowaniem, a to jest jednym z wielkich problemów z linq do sql - po prostu nie wiesz co może się zdarzyć, że śledzą inżynierowie wymyślają lepsze sposoby robienia rzeczy.

Zrobiłbym to w przechowywanym proc. Jeśli chcesz, zawsze możesz zwrócić zestaw wyników do wcześniej zdefiniowanej tabeli.

+0

Szczerze mówiąc, nie testowałem rozwiązania, które dostarczyłem, używając tabel tymczasowych. Biorąc to pod uwagę, rozwiązanie na pewno zadziała przy użyciu "stałych" tabel. Ponadto powodem, dla którego używam metody DataContext.ExecuteCommand() jest to, że instrukcja SQL nie jest w ogóle obsługiwana przez silnik LINQ ... to, co wysyłasz, jest uruchamiane. –

Powiązane problemy