Исходный код для примеров LINQ to SQL
LINQ --- LINQ to DataSet и SQL --- Исходный код для примеров LINQ to SQL
Предварительные условия для запуска примеров
Поскольку почти во всех примерах, посвященных LINQ to SQL, используется расширенная база данных примеров Northwind, понадобятся сущностные классы и файлы отображения для базы данных Northwind.
Получение соответствующей версии базы данных Northwind
К сожалению, в стандартной базе данных Northwind недостает нескольких вещей, которые понадобятся для полноценной демонстрации LINQ to SQL, таких как табличные и скалярные функции. Поэтому вместо стандартной базы данных Northwind применяется расширенная версия, поставляемая Microsoft для демонстрации возможностей LINQ.
Скачать данную базу вы можете по следующей ссылке - Extended Northwind Database.
Создание подключений к базе данных "Northwind"
Откройте обозреватель серверов, выбрав команду в меню Вид --> Обозреватель серверов (View --> Server Explorer).
Щелкните правой кнопкой мыши Подключения данных (Data Connections) и выберите пункт Добавить подключение (Add Connection).
В поле Источник данных выберите Microsoft SQL Server. В зависимости от требований базы данных или приложения нажмите кнопку Использовать проверку подлинности Windows или используйте указанные имя пользователя и пароль для входа в систему на компьютере с SQL Server (Использовать проверку подлинности SQL Server). В поле Прикрепить файл базы данных укажите ссылку на файл базы данных Northwind.mdf.
Диалоговое окно Добавить подключение должно выглядеть примерно так:
Нажмите кнопку ОК.
Генерация сущностных классов 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 или как-нибудь еще.
Запуск этой команды вызовет создание модуля сущностного класса по имени 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));
}
}
}