Перейти к содержанию

Исходный код для примеров LINQ to SQL

Предварительные условия для запуска примеров

Поскольку почти во всех примерах, посвященных LINQ to SQL, используется расширенная база данных примеров Northwind, понадобятся сущностные классы и файлы отображения для базы данных Northwind.

Получение соответствующей версии базы данных Northwind

К сожалению, в стандартной базе данных Northwind недостает нескольких вещей, которые понадобятся для полноценной демонстрации LINQ to SQL, таких как табличные и скалярные функции. Поэтому вместо стандартной базы данных Northwind применяется расширенная версия, поставляемая Microsoft для демонстрации возможностей LINQ.

Скачать данную базу вы можете по следующей ссылке - Extended Northwind Database.

Создание подключений к базе данных "Northwind"

Генерация сущностных классов Northwind

Чтобы сгенерировать сущностные классы, должна быть доступна расширенная версия базы данных Northwind, как упоминалось в предыдущем разделе.

Откройте окно командной строки Visual Studio, выбрав в меню пункт Microsoft Visual Studio 2010 --> Visual Studio Tools --> Visual Studio 2010 Command Prompt (Microsoft Visual Studio 2010 --> Инструменты Visual Studio --> Командная строка Visual Studio 2010). В открывшемся окне командной строки смените текущий каталог на тот, в котором должны быть созданы сущностные классы и внешние файлы отображения, например:

cd C:\myProject\LINQ to DataSet and SQL\LINQ to DataSet and SQL

Если вы собираетесь генерировать сущностные классы с использованием файлов базы данных Northwind, не подключая к ним базу данных, используйте следующую команду:

sqlmetal /namespace:nwind /code:Northwind.cs /pluralize /functions /sprocs /views
<путь к MDF-файлу Northwind>

Уделите особое внимание имени файла MDF и регистру его символов, указывая его в командной строке. Имя и регистр генерируемого производного от DataContext класса будет соответствовать имени файла, переданному в командной строке, а не физическому имени самого файла. Если вы отступите от точного написания имени класса Northwind, то ни один пример не станет работать без модификации. Поэтому важно передать имя файла базы данных, как [путь]\Northwind.mdf, а не northwind.mdf, NorthWind.mdf или как-нибудь еще.

Генерация сущностных классов из mdf-файла

Запуск этой команды вызовет создание модуля сущностного класса по имени Northwind.cs в текущем каталоге.

Если вы собираетесь генерировать сущностные классы из базы данных Northwind, которая уже подключена к SQL Server, используйте следующую команду:

sqlmetal /server:<сервер> /user:<пользователь> /password:<пароль> 
/database:Northwind /namespace:nwind /code:Northwind.cs 
/pluralize /functions /sprocs /views

Чтобы создать сущностные классы из подключенной базы данных по имени Northwind, введите следующую команду:

Генерация сущностных классов из базы данных

В зависимости от окружения, в командной строке может понадобиться указать пользователя с помощью опции /user: [пользователь] и пароль — посредством опции /password: [пароль].

Команда, введенная с использованием любого из этих подходов, сообщает утилите SQLMetal о том, что она должна сгенерировать файл исходного кода по имени Northwind.cs в текущем каталоге. Все опции этой утилиты будут описаны в следующей статье. Скопируйте сгенерированный файл Northwind.cs в проект, добавив его в качестве существующего элемента.

Теперь можно применять LINQ to SQL на базе данных Northwind, используя сущностные классы, которые содержатся в файле Northwind.cs.

Будьте осторожны, внося изменения в исходный файл сгенерированных сущностных классов. Позднее может возникнуть необходимость в их повторной генерации, что вызовет потерю этих изменений. Может понадобиться расширить бизнес-логику за счет добавления методов к сущностным классам. Вместо модификации сгенерированного файла рассмотрите возможность применения частичных классов C#, чтобы хранить добавленные свойства и методы в отдельном модуле исходного кода.

Генерация XML-файла отображения Northwind

Также понадобится сгенерировать файл отображения, который будет использоваться в некоторых примерах. Опять-таки, для этого применяется утилита SQLMetal. В том же текущем каталоге выполните следующую команду:

sqlmetal /map:northwindmap.xml "С:\Northwind.mdf" /pluralize /functions /sprocs /views /namespace:nwind /coderNorthwind.cs

При указании MDF-файла обратите особое внимание на регистр символов. Эта команда приведет к генерации файла по имени northwindmap.xml в текущем каталоге.

Использование LINQ to SQL

Чтобы можно было пользоваться API-интерфейсом LINQ to SQL, в проект понадобится добавить сборку System.Data.Linq.dll, если это не было сделано ранее. Кроме того, в исходный код должны быть добавлены директивы using для пространств имен System.Linq и System.Data.Linq:

using System.Data.Linq; 
using System.Linq;

В коде примеров потребуется добавить директиву using для пространства имен, в котором находятся сгенерированные сущностные классы:

using nwind;

XQueryable<T>

Вы увидите, что во многих примерах LINQ to SQL работа производится с последовательностями типа IQueryable<T>, где T — тип сущностного класса. Это тип последовательностей, которые обычно возвращаются запросами LINQ to SQL. Они часто работают подобно последовательности IEnumerable<T>, и это не случайно. Интерфейс IQueryable<T> реализует интерфейс IEnumerable<T>. Вот его определение:

interface IQueryable<T> : IEnumerable<T>, IQueryable

Благодаря такому наследованию, последовательность IQueryable<T> можно трактовать как последовательность IEnumerable<T>.

Некоторые общие методы

Как вы вскоре убедитесь, примеры, посвященныe LINQ to SQL, очень быстро усложняются. Демонстрация конфликтов параллельного доступа требует внесения изменений в базу данных, внешнюю по отношению к LINQ to SQL. Иногда приходится извлекать данные извне LINQ to SQL. Чтобы подчеркнуть код LINQ to SQL и исключить как можно больше тривиальных деталей, в то же время сохраняя реальную пользу примеров, создано несколько общих методов, предназначенных для использования во многих примерах.

Не забудьте добавить эти общие методы в исходные модули при тестировании примеров, посвященных LINQ to SQL.

GetStringFromDb()

Общий метод, который пригодится не раз — это метод для получения простой строки из базы данных с использованием стандартной технологии ADO.NET. Он позволит просматривать то, что в действительности имеется в базе данных, чтобы сравнить с тем, что показывает LINQ to SQL.

static private string GetStringFromDb(
            System.Data.SqlClient.SqlConnection sqlConnection, string sqlQuery)
        {
            if (sqlConnection.State != System.Data.ConnectionState.Open)
            {
                sqlConnection.Open();
            }

            System.Data.SqlClient.SqlCommand sqlCommand =
              new System.Data.SqlClient.SqlCommand(sqlQuery, sqlConnection);

            System.Data.SqlClient.SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            string result = null;

            try
            {
                if (!sqlDataReader.Read())
                {
                    throw (new Exception(
                      String.Format("Выдано исключение [{0}].", sqlQuery)));
                }
                else
                {
                    if (!sqlDataReader.IsDBNull(0))
                    {
                        result = sqlDataReader.GetString(0);
                    }
                }
            }
            finally
            {
                // Всегда закрывать по окончании чтения
                sqlDataReader.Close();
            }

            return (result);
        }

При вызове методу GetStringFromDb передается объект SqlConnection и строка, содержащая запрос SQL. Метод проверяет, открыто ли соединение, и если нет, то открывает его.

После этого создается экземпляр SqlCommand передачей конструктору запроса и соединения. Затем в результате вызова метода ExecuteReader класса SqlCommand получается SqlDataReader. Экземпляр SqlDataReader выполняет чтение вызовом метода Read, и если данные прочитаны и возвращенное значение первого столбца не равно null, это значение извлекается методом GetString. Наконец, SqlDataReader закрывается, и значение первого столбца возвращается вызвавшему методу.

ExecuteStatementInDb()

Иногда нужно будет выполнять операторы SQL, не являющиеся запросами, такие как вставка, обновление и удаление в ADO.NET, чтобы модифицировать состояние базы данных вне LINQ to SQL. Для этой цели предусмотрен метод ExecuteStatementInDb():


    static private void ExecuteStatementInDb(string cmd)
    {
      // Здесь укажите свою строку подключения
      string connection =
        @"Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=SSPI;";

      System.Data.SqlClient.SqlConnection sqlConn =
        new System.Data.SqlClient.SqlConnection(connection);

      System.Data.SqlClient.SqlCommand sqlComm =
        new System.Data.SqlClient.SqlCommand(cmd);

      sqlComm.Connection = sqlConn;
      try
      {
        sqlConn.Open();
        Console.WriteLine("Выполнение оператора SQL длябазы данных с помощью ADO.NET ...");
        sqlComm.ExecuteNonQuery();
        Console.WriteLine("Database updated.");
      }
      finally
      {
        // Закрыть соединение
        sqlComm.Connection.Close();
      }
    }

При вызове методу ExecuteStatementinDb передается строка, содержащая команду SQL. Создаются экземпляры SqlConnection и SqlCommand. Экземпляру SqlCommand назначается SqlConnection. Затем SqlConnection открывается и команда SQL выполняется вызовом метода ExecuteNonQuery объекта SqlCommand. И, наконец, SqlConnection закрывается.

Тестовый проект на WPF

В качестве наглядной демонстрации некоторых примеров использования LINQ to SQL, будет использоваться тестовый проект на платформе WPF, включающий элемент DataGrid, в котором будет отображена таблица Customers из базы данных Northwind. XAML-разметка и исходный код выглядят следующим образом:

<Window x:Class="nwind.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LINQ to SQL" Height="600" Width="800">
   <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ScrollViewer HorizontalScrollBarVisibility="Auto">
                <DataGrid Margin="10" x:Name="dataGrid" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="ID" Binding="{Binding Path=CustomerID}"/>
                        <DataGridTextColumn Header="CompanyName" Binding="{Binding Path=CompanyName}"/>
                        <DataGridTextColumn Header="ContactName" Binding="{Binding Path=ContactName}"/>
                        <DataGridTextColumn Header="ContactTitle" Binding="{Binding Path=ContactTitle}"/>
                        <DataGridTextColumn Header="Address" Binding="{Binding Path=Address}"/>
                        <DataGridTextColumn Header="City" Binding="{Binding Path=City}"/>
                        <DataGridTextColumn Header="Region" Binding="{Binding Path=Region}"/>
                        <DataGridTextColumn Header="PostalCode" Binding="{Binding Path=PostalCode}"/>
                        <DataGridTextColumn Header="Country" Binding="{Binding Path=Country}"/>
                        <DataGridTextColumn Header="Phone" Binding="{Binding Path=Phone}"/>
                        <DataGridTextColumn Header="Fax" Binding="{Binding Path=Fax}"/>
                    </DataGrid.Columns>
                </DataGrid>
            </ScrollViewer>
        <TextBox x:Name="txt_log" Grid.Row="1" TextWrapping="Wrap" MinHeight="80"/>
    </Grid>
</Window>
using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace nwind
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Используйте свою строку подключения
            Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;AttachDbFilename=C:\Northwind.mdf;Integrated Security=True");
            dataGrid.ItemsSource = GetCustomersForDataGrid(db);
        }

        public ObservableCollection<Customer> GetCustomersForDataGrid(Northwind db)
        {
            IEnumerable<Customer> custs = (from c in db.Customers
                                           select c).AsEnumerable();
            ObservableCollection<Customer> obs = new ObservableCollection<Customer>(custs);
            return obs;
        }
    }
    
    // Класс для протоколирования SQL-запросов
    public class TextBoxWriter : TextWriter
    {
        private Encoding encoding;
        private TextBox textBox;  
  
        public TextBoxWriter(TextBox textBox)
        {
           if (textBox == null)
              throw new NullReferenceException() ;
           this.textBox = textBox;
        }   
        
        public override Encoding Encoding
        {
            get
            {
                if (this.encoding == null)
                {
                    this.encoding = new UnicodeEncoding(false, false);
                }
                return encoding;
            }
        } 
        
        public override void Write(string value)
        {
            this.textBox.AppendText(value);
        }
        
        public override void Write(char[] buffer)
        {
            this.Write(new string(buffer));
        }   
        
        public override void Write(char[] buffer, int index, int count)
        {
            this.Write(new string(buffer, index, count));
        }
    }
}
Тестовый пример использования элемента DataGrid