Исходный код для примеров 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
Пройди тесты
Лучший чат для C# программистов