Временные таблицы в Linq - Кто-нибудь видит проблему с этим? - PullRequest
7 голосов
/ 17 июня 2009

При попытке решить:

Linq. Содержит с большим набором причин Ошибка TDS

Я думаю, что наткнулся на решение, и я хотел бы посмотреть, если это кошерный способ решения проблемы.

(краткое резюме) Я хотел бы объединить linq-join со списком идентификаторов записей, которые (полностью или хотя бы легко) не генерируются в SQL. Это большой список, который часто выходит за пределы 2100 пунктов для вызова TDS RPC. Поэтому то, что я сделал в 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 ]

3 голосов
/ 17 сентября 2010

Я не вижу проблемы с использованием временных таблиц для решения вашей проблемы. Что касается смешивания SqlCommands и LINQ, вы абсолютно правы относительно фактора опасности. Это так легко выполнить ваши операторы SQL, используя DataContext, я бы даже не беспокоился о 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 голос
/ 05 октября 2010

У нас похожая ситуация, и хотя это работает, возникает проблема, что вы на самом деле не имеете дело с Queryables, поэтому вы не можете легко использовать это "с" LINQ. Это не решение, которое работает с цепочками методов.

Наше окончательное решение состояло в том, чтобы просто выбросить то, что мы хотим, в хранимой процедуре, и записать выборки в этой процедуре во временные таблицы, когда нам нужны эти значения. Это компромисс, но оба обходные пути. По крайней мере, с сохраненным процессом разработчик сгенерирует вызывающий код для вас, и у вас будет реализация в черном ящике, поэтому, если вам потребуется выполнить дополнительную настройку, вы можете сделать это строго в рамках процедуры, без перекомпиляции.

В идеальном мире в будущем появится некоторая поддержка написания операторов Linq2Sql, которые позволят вам диктовать использование временных таблиц в ваших запросах, избегайте неприятного оператора sql IN для сложных сценариев, подобных этому.

0 голосов
/ 20 октября 2010

Будет ли решение, предлагаемое Нилом, действительно работать? Если это временная таблица, и каждый из методов создает и удаляет свой собственный контекст данных, я не думаю, что временная таблица все еще будет там после разрыва соединения.

Даже если бы это было так, я думаю, что это будет область, где вы предполагаете некоторую функциональность того, как запросы и соединения будут отображаться, и это одна из больших проблем с linq to sql - вы просто не знаете, что случается, он отслеживает, поскольку инженеры придумывают лучшие способы сделать вещи.

Я бы сделал это в хранимой процедуре. Вы всегда можете вернуть набор результатов в заранее определенную таблицу, если хотите.

0 голосов
/ 17 сентября 2010

Как «универсальное решение», что если код запускается в нескольких потоках / приложениях? Я думаю, что решение большого списка всегда связано с проблемной областью. Лучше использовать обычную таблицу для задачи, над которой вы работаете.

Однажды я создал «общую» таблицу списка в базе данных. Таблица была создана с тремя столбцами: int, uniqueidentifier и varchar, а также другими столбцами для управления каждым списком. Я думал: «Этого должно быть достаточно, чтобы справиться со многими случаями». Но вскоре я получил задание, которое требует выполнения соединения со списком из трех целых чисел. После этого я больше никогда не пытался создать «общую» таблицу списков.

Кроме того, лучше создать SP, чтобы вставлять несколько элементов в таблицу списка при каждом вызове базы данных. Вы можете легко вставить ~ 2000 предметов менее чем за 2 дБ. Конечно, в зависимости от того, что вы делаете, производительность может не иметь значения.

РЕДАКТИРОВАТЬ: забыл, что это временная таблица и временная таблица для каждого соединения, поэтому мой предыдущий аргумент для многопоточности был неверным. Но, тем не менее, это не общее решение для применения фиксированной схемы.

...