• Microsoft .NET
  • LINQ
  • Часто задаваемые вопросы о LINQ - часть вторая

Часто задаваемые вопросы о LINQ - часть вторая

ОГЛАВЛЕНИЕ

Введение и цель

Эта статья вопросов-ответов посвящена LINQ для SQL. Показан простой пример LINQ для SQL, как определить отношение 1-1 и 1-много с помощью LINQ, как можно оптимизировать запросы LINQ, выполнение хранимых процедур с помощью LINQ и простой пример создания, чтения, обновления, удаления (CRUD) с помощью LINQ для SQL.

Простой пример LINQ для SQL

Начнем с простого примера LINQ для SQL, а затем узнаем, как устанавливается отношение в сущностях LINQ.

Шаг 1: Определяются классы-сущности с помощью LINQ

При разработке проекта с помощью многоуровневого подхода, например, 3-уровневого или N-уровневого, приходится создавать бизнес-классы и объекты. Например, ниже показан простой класс, определяющий класс, сопоставленный с таблицей стран, как показано ниже. Свойства класса взаимно-однозначно сопоставлены с таблицей. Такие типы классов именуются классы-сущности.

В LINQ необходимо сначала определить эти классы-сущности с помощью сопоставлений атрибутов. Надо импортировать пространство имен “System.Data.Linq.Mapping;”, чтобы получить атрибуты для сопоставления. Ниже приведен фрагмент кода, показывающий, как атрибут ‘Таблица’ сопоставляет класс с таблицей базы данных по имени ‘Клиент’, и как атрибут ‘Столбец’ сопоставляет свойства со столбцами таблицы.

[Table(Name = "Customer")]
public class clsCustomerEntityWithProperties
{
private int _CustomerId;
private string _CustomerCode;
private string _CustomerName;

[Column(DbType = "nvarchar(50)")]
public string CustomerCode
{
set
{
_CustomerCode = value;
}
get
{
return _CustomerCode;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerName
{
set
{
_CustomerName = value;
}
get
{
return _CustomerName;
}
}

[Column(DbType = "int", IsPrimaryKey = true)]
public int CustomerId
{
set
{
_CustomerId = value;
}
get
{
return _CustomerId;
}
}
}

Ниже дано более сложное наглядное изображение сопоставления классов-сущностей со структурой таблицы клиент.

Шаг 2: Используется datacontext для связывания данных таблицы с объектами-сущностями.

Второй шаг – использовать объект контекста данных LINQ для заполнения объектов-сущностей. Datacontext служит посредником между объектами базы данных и классами, сопоставленными с сущностями LINQ.

Создается объект datacontext и создается активное подключение с помощью строки подключения SQL.

DataContext objContext = new DataContext(strConnectionString);

Извлекается коллекция сущностей с помощью типа данных таблица. Это делается с помощью функции ‘gettable’ контекста данных.

Table<clsCustomerEntity> objTable =
objContext.GetTable<clsCustomerEntity>();

После извлечения всех данных в коллекции таблицы пора просмотреть коллекцию таблицы и отобразить запись.

foreach (clsCustomerEntity objCustomer in objTable)
{
Response.Write(objCustomer.CustomerName + "<br>");
}

Вышеприведенный исходный код приложен к статье.

Можно ли инкапсулировать свойства установки и извлечения для сущностей LINQ?

В предыдущем вопросе свойства класса-сущности предоставлялись как открытые свойства, что нарушало основное правило инкапсуляции. Можно определить функции сеттер (установщик) и геттер (извлекатель), инкапсулирующие закрытые свойства.

[Table(Name = "Customer")]
public class clsCustomerEntityWithProperties
{
private int _CustomerId;
private string _CustomerCode;
private string _CustomerName;

[Column(DbType = "nvarchar(50)")]
public string CustomerCode
{
set
{
_CustomerCode = value;
}
get
{
return _CustomerCode;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerName
{
set
{
_CustomerName = value;
}
get
{
return _CustomerName;
}
}

[Column(DbType = "int", IsPrimaryKey = true)]
public int CustomerId
{
set
{
_CustomerId = value;
}
get
{
return _CustomerId;
}
}
}

Простой пример отношений 1-1 и 1-много.

LINQ помогает определять отношения с помощью ‘EntitySet’ и ‘EntityRef’. Чтобы понять, как определяются отношения с помощью LINQ, рассмотрим пример ниже, где имеется клиент, имеющий много адресов, а каждый адрес имеет номер телефона. То есть клиент и адрес имеют отношение один-к-многим, тогда как адрес и номер телефона имеют отношение один-к-одному.

Чтобы определить отношение один-к-многим между классами клиента и адреса, надо использовать атрибут ‘EntitySet’. Чтобы определить отношение один-к-одному между классами адреса и телефона, надо использовать атрибут ‘EntityRef’.

Замечание: Надо определить атрибут первичного ключа для каждого класса-сущности, иначе отношение сопоставления не будет работать.

Ниже приведен фрагмент сущности класса для класса клиента, показывающий, как используется ‘EntitySet’ для определения отношения один-к-многим с классом адреса. Ассоциация определяется с помощью атрибута ‘Association’. Атрибут ‘Association’ имеет три важных свойства: storage, thiskey и otherkey. ‘storage’ определяет имя закрытой переменной, где хранится объект адреса, сейчас это ‘_CustomerAddresses’. ‘ThisKey’ и ‘OtherKey’ определяют, какое свойство определяет связь, в данном примере это ‘CustomerId’. То есть класс ‘Customer’ и класс ‘Address’ имеют общее свойство ‘CustomerId’. ’ThisKey’ определяет имя свойства для класса клиента, тогда как ‘OtherKey’ определяет свойство класса адреса.

[Table(Name = "Customer")]
public class clsCustomerWithAddresses
{

private EntitySet<clsAddresses> _CustomerAddresses;

[Association(Storage = "_CustomerAddresses",ThisKey="CustomerId", OtherKey = "CustomerId")]
public EntitySet<clsAddresses> Addresses
{
set
{
_CustomerAddresses = value;
}
get
{
return _CustomerAddresses;
}
}
}

Ниже дан полный фрагмент кода с другими свойствами класса клиента.

[Table(Name = "Customer")]
public class clsCustomerWithAddresses
{
private int _CustomerId;
private string _CustomerCode;
private string _CustomerName;
private EntitySet<clsAddresses> _CustomerAddresses;

[Column(DbType="int",IsPrimaryKey=true)]
public int CustomerId
{
set
{
_CustomerId = value;
}
get
{
return _CustomerId;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerCode
{
set
{
_CustomerCode = value;
}
get
{
return _CustomerCode;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerName
{
set
{
_CustomerName = value;
}
get
{
return _CustomerName;
}
}

[Association(Storage = "_CustomerAddresses",ThisKey="CustomerId", OtherKey = "CustomerId")]
public EntitySet<clsAddresses> Addresses
{
set
{
_CustomerAddresses = value;
}
get
{
return _CustomerAddresses;
}

}
}

Чтобы определить отношение между классом адреса и классом телефона, надо использовать синтаксис ‘EntityRef’. Ниже приведен фрагмент кода, определяющий отношение с помощью ‘EntityRef’. Все остальные свойства такие же, кроме того, что переменная определяется с помощью ‘EntityRef’.

public class clsAddresses
{

private int _AddressId;
private EntityRef<clsPhone> _Phone;

[Column(DbType = "int", IsPrimaryKey = true)]
public int AddressId
{
set
{
_AddressId = value;
}
get
{
return _AddressId;
}
}
[Association(Storage = "_Phone",
ThisKey = "AddressId", OtherKey = "AddressId")]
public clsPhone Phone
{
set
{
_Phone.Entity = value;
}
get
{
return _Phone.Entity;
}
}
}

Ниже приведен полный класс адреса с остальными свойствами.

public class clsAddresses
{
private int _Customerid;
private int _AddressId;
private string _Address1;
private EntityRef<clsPhone> _Phone;
[Column(DbType="int")]
public int CustomerId
{
set
{
_Customerid = value;
}
get
{
return _Customerid;
}
}
[Column(DbType = "int", IsPrimaryKey = true)]
public int AddressId
{
set
{
_AddressId = value;
}
get
{
return _AddressId;
}
}
[Column(DbType = "nvarchar(50)")]
public string Address1
{
set
{
_Address1 = value;
}
get
{
return _Address1;
}
}
[Association(Storage = "_Phone",
ThisKey = "AddressId", OtherKey = "AddressId")]
public clsPhone Phone
{
set
{
_Phone.Entity = value;
}
get
{
return _Phone.Entity;
}
}
}

Класс телефона, объединенный с классом адреса.

[Table(Name = "Phone")]
public class clsPhone
{
private int _PhoneId;
private int _AddressId;
private string _MobilePhone;
private string _LandLine;

[Column(DbType = "int", IsPrimaryKey = true)]
public int PhoneId
{
set
{
_PhoneId = value;
}
get
{
return _PhoneId;
}
}
[Column(DbType = "int")]
public int AddressId
{
set
{
_PhoneId = value;
}
get
{
return _PhoneId;
}
}
[Column(DbType = "nvarchar")]
public string MobilePhone
{
set
{
_MobilePhone = value;
}
get
{
return _MobilePhone;
}
}
[Column(DbType = "nvarchar")]
public string LandLine
{
set
{
_LandLine = value;
}
get
{
return _LandLine;
}
}
}

Теперь надо использовать это отношение в выделенном коде клиента ASPX.

Первый шаг - создать объект контекста данных с инициализированным подключением.

DataContext objContext = new DataContext(strConnectionString);

Второй шаг – запустить запрос. Только запускается запрос для класса клиента. Механизм LINQ следит за тем, чтобы все данные дочерних таблиц были извлечены и размещены согласно отношению, определенному в классах-сущностях.

var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;

Наконец, проходим в цикле по клиентам, проходим в цикле по соответствующему объекту адресов и выводим на экран номер телефона согласно объекту телефона.

foreach (clsCustomerWithAddresses objCustomer in MyQuery)
{
Response.Write(objCustomer.CustomerName + "<br>");
foreach (clsAddresses objAddress in objCustomer.Addresses)
{
Response.Write("===Address:- " + objAddress.Address1 + "<br>");
Response.Write("========Mobile:- " + objAddress.Phone.MobilePhone + "<br>");
Response.Write("========LandLine:- " + objAddress.Phone.LandLine + "<br>");
}
}

Вывод выглядит так, как показано ниже. Каждый клиент имеет несколько адресов, и каждый адрес имеет объект телефона.


Как происходят полные обходы в LINQ

Сначала разберем, как на самом деле работают запросы LINQ, а затем узнаем, как происходят полные обходы. Рассмотрим следующую структуру базы данных с 3 таблицами – customer(клиент), addresses(адреса) и phone(телефон). Есть отношение один-к-многим между клиентом и адресами, а между таблицами адресов и телефонов есть отношение один-к-одному.

Были созданы три сущности согласно структуре таблицы, т.е. ‘ClsCustomerWithAddresses’,’ClsAddresses’ и ‘ClsPhone’. Были определены отношения между ними с помощью ‘EntitySet’ и ‘EntityRef’.

Заполнение объектов-сущностей данными из таблицы – процесс из 5 шагов. На первом шаге создается подключение datacontext с помощью строки подключения, создается запрос LINQ, а затем начинается просмотр клиента, адреса и телефонов.

Анализ полных обходов LINQ SQL

Было разобрано 5 шагов выполнения запроса LINQ. Выясним, на каком шаге запрос LINQ фактически запускает SQL в базе данных. Запустим вышеуказанный код LINQ и проанализируем его с помощью профилировщика SQL.

Чтобы не поймать кучу шума сервера SQL, были включены только групповые события RPC и SQL.

Теперь при выполнении запроса выясняется следующее:
• Выполнение реального SQL происходит, когда инструкция foreach повторяется над объектами LINQ.
• Для каждой сущности запускается отдельный запрос к серверу SQL. Например, запускается один запрос для клиента, и затем запускаются отдельные запросы для адреса и телефонов, чтобы вырастить объект сущности. То есть много полных обходов.

Как избежать лишних полных обходов

Можно приказать механизму LINQ загружать все объекты с помощью ‘DataLoadOptions’. Ниже описаны шаги по включению ‘DataLoadOptions’.

Первый шаг – создать класс контекста данных.

DataContext objContext = new DataContext(strConnectionString);

Второй шаг – создать объект ‘DataLoadOption’.

DataLoadOptions objDataLoadOption = new DataLoadOptions();

С помощью метода LoadWith надо определить загрузку клиента с адресом в одном SQL.

objDataLoadOption.LoadWith<clsCustomerWithAddresses>(clsCustomerWithAddresses => clsCustomerWithAddresses.Addresses);

Каждый объект адреса имеет объект телефона, поэтому была определена опция, говорящая, что объекты телефона должны загружаться для каждого объекта адреса в одном SQL.

objDataLoadOption.LoadWith<clsAddresses>(clsAddresses => clsAddresses.Phone);

Какая бы опция загрузки ни была определена, надо установить то же самое в объекте контекста данных с помощью свойства ‘LoadOptions’.

objContext.LoadOptions = objDataLoadOption;

Наконец, готовится запрос.

var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;

Начинается обход объектов в цикле.

foreach (clsCustomerWithAddresses objCustomer in MyQuery)
{

Response.Write(objCustomer.CustomerName + "<br>");

foreach (clsAddresses objAddress in objCustomer.Addresses)
{
Response.Write("===Address:- " + objAddress.Address1 + "<br>");
Response.Write("========Mobile:- " + objAddress.Phone.MobilePhone + "<br>");
Response.Write("========LandLine:- " + objAddress.Phone.LandLine + "<br>");
}
}

Ниже приведен полный исходный код для того же самого.

DataContext objContext = new DataContext(strConnectionString);
DataLoadOptions objDataLoadOption = new DataLoadOptions();
objDataLoadOption.LoadWith<clsCustomerWithAddresses>(clsCustomerWithAddresses => clsCustomerWithAddresses.Addresses);
objDataLoadOption.LoadWith<clsAddresses>(clsAddresses => clsAddresses.Phone);
objContext.LoadOptions = objDataLoadOption;
var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;

foreach (clsCustomerWithAddresses objCustomer in MyQuery)
{

Response.Write(objCustomer.CustomerName + "<br>");

foreach (clsAddresses objAddress in objCustomer.Addresses)
{
Response.Write("===Address:- " + objAddress.Address1 + "<br>");
Response.Write("========Mobile:- " + objAddress.Phone.MobilePhone + "<br>");
Response.Write("========LandLine:- " + objAddress.Phone.LandLine + "<br>");
}
}

Теперь при запуске кода LINQ выполняет только один SQL с должными соединениями по сравнению с 3 SQL для каждого объекта, показанными ранее.

Исходный код

К данной статье был приложен исходный код. Запустите проект и посмотрите, как профилировщик показывает разное выполнение SQL. Сначала запустите пример ‘EntitySet’ и посмотрите, как профилировщик SQL реагирует на то же самое, а затем запустите пример с ‘DataLoadOptions’. Скрипт SQL приложен в отдельном файле.


Как выполняются хранимые процедуры с помощью LINQ

Шаг 1: Создать хранимую процедуру

Ниже приведена хранимая процедура, используемая для выращивания объектов LINQ.

Create PROCEDURE dbo.usp_SelectCustomer
AS
Select CustomerId,CustomerCode,CustomerName from Customer
RETURN

Шаг 2: Создать сущность LINQ

Вышеуказанная хранимая процедура возвращает ‘CustomerId’,’CustomerCode’ и ‘CustomerName’, поэтому надо приготовить сущность LINQ согласно данным, возвращаемым хранимой процедурой. Если вы не знакомы с сущностями LINQ, прочитайте основы в OneManyandOneOneLINQ.aspx

[Table(Name = "Customer")]
public class clsCustomerEntity
{
private int _CustomerId;
private string _CustomerCode;
private string _CustomerName;

[Column(DbType = "nvarchar(50)")]
public string CustomerCode
{
set
{
_CustomerCode = value;
}
get
{
return _CustomerCode;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerName
{
set
{
_CustomerName = value;
}
get
{
return _CustomerName;
}
}

[Column(DbType = "int", IsPrimaryKey = true)]
public int CustomerId
{
set
{
_CustomerId = value;
}
get
{
return _CustomerId;
}
}
}

Шаг 3: Унаследовать от класса DataContext

Для выполнения хранимых процедур LINQ предоставляет функцию вызова ‘ExecuteMethod’, принадлежащую классу ‘DataContext’. Эта функция возвращает ‘ISingleresult’ коллекции сущностей. Функция вызова ‘ExecuteMethod’ – защищенная функция и вызывается только через наследование. Методы и функции, из которых вызываются хранимые процедуры, формируют DAL. То есть ‘ExecuteMethod’ должен входить в DAL.

Как сказано, функция чисто защищенная, вызываемая только путем наследования, а не агрегирования. Неясно, почему Microsoft принуждает к этому, поэтому надо создать еще один лишний класс, наследуемый от ‘DataContext’, а затем вставить соответствующие вызовы функции для хранимых процедур. Ниже приведен фрагмент кода, в котором наследуется от класса ‘DataContext’ и создается новый класс DAL по имени ‘ClsMyContext’.

public class clsMyContext : DataContext
{}

Шаг 4: Приписать с помощью атрибута Функция

Была создана функция ‘GetCustomerAll’, приписанная с помощью атрибута ‘Function’ из пространства имен ‘System.Data.Linq.Mapping’. Атрибут ‘Function’ имеет параметр имя, задающий имя хранимой процедуры; сейчас хранимая процедура - ‘usp_SelectCustomer’, как определено в предыдущих шагах.

Параметр ‘IsComposable’ определяет, предназначен ли этот вызов метода (для или) для UDF, т.е. определённой пользователем функции. Если ‘IsComposable’ равен false, то это хранимая процедура, а если равен true, то это определённая пользователем функция.

[Function(Name = "usp_SelectCustomer", IsComposable = false)]

public ISingleResult<clsCustomerEntity> getCustomerAll()
{
}

Шаг 5: Вызвать вызов Executemethod

Пора заполнить пустую функцию ‘GetCustomerAll’. Ниже приведен фрагмент кода, выполняющий вызов ‘ExecuteMethod’. Этот вызов возвращает объект ‘IExecuteResult’.

IExecuteResult objResult = this.ExecuteMethodCall(this,(MethodInfo)(MethodInfo.GetCurrentMethod()));

Объект, возвращенный из ‘IExecuteResult’, имеет свойство ‘ReturnValue’, из которого можно получить коллекцию результатов типа ‘ClsCustomerEntity’.

ISingleResult<clsCustomerEntity> objresults = (ISingleResult<clsCustomerEntity>) objResult.ReturnValue;

Ниже приведен полный фрагмент кода с функцией.

[Function(Name = "usp_SelectCustomer", IsComposable = false)]
public ISingleResult<clsCustomerEntity> getCustomerAll()
{
IExecuteResult objResult = this.ExecuteMethodCall(this,(MethodInfo)(MethodInfo.GetCurrentMethod()));

ISingleResult<clsCustomerEntity> objresults = (ISingleResult<clsCustomerEntity>) objResult.ReturnValue;
return objresults;
}

Шаг 6: Вызвать контекст данных в клиенте

На последнем шаге создается объект контекста, вызывается функция, и коллекция объектов обходится в цикле с выводом данных на экран.

clsMyContext objContext = new clsMyContext(strConnectionString);
foreach(var row in objContext.getCustomerAll())
{
Response.Write(row.CustomerCode);
}

Объясните фиксацию в памяти и физическую фиксацию LINQ

Объекты-сущности образуют основу технологий LINQ. Поэтому при отправке любых данных в базу данных они проходят через объекты LINQ. Операции базы данных делаются посредством класса ‘DataContext’. Как сказано выше, сущности образуют основу LINQ, поэтому все данные сначала отправляются этим сущностям и затем направляются в реальную физическую базу данных. Из-за этого фиксация транзакции базы данных является двухэтапным процессом: первый этап происходит в памяти, а второй этап – физическая фиксация.

Для выполнения операции в памяти ‘DataContext’ предоставляет методы ‘DeleteOnSubmit’ и ‘InsertOnSubmit’. Когда эти методы вызываются из класса ‘DataContext’, они добавляют и обновляют данные в памяти объектов-сущностей. Учтите, что эти методы не меняют / не добавляют новые данные в реальную базу данных.

После завершения операций в памяти вызывается метод ‘SubmitChanges()’ для отправки всех обновлений в базу данных. Этот метод фиксирует данные в физической базе данных.

Рассмотрим таблицу пользователей (customerid, customercode и customername) и посмотрим, как делаются операции фиксации в памяти и физической фиксации.


Простой пример CRUD с помощью LINQ

Шаг 1: Создать сущность класса «Клиент»

На первом шаге создается сущность класса «Клиент», как показано в следующем фрагменте кода.

[Table(Name = "Customer")]
public class clsCustomerEntity
{
private int _CustomerId;
private string _CustomerCode;
private string _CustomerName;

[Column(DbType = "nvarchar(50)")]
public string CustomerCode
{
set
{
_CustomerCode = value;
}
get
{
return _CustomerCode;
}
}

[Column(DbType = "nvarchar(50)")]
public string CustomerName
{
set
{
_CustomerName = value;
}
get
{
return _CustomerName;
}
}

[Column(DbType = "int", IsPrimaryKey = true,IsDbGenerated=true)]
public int CustomerId
{
set
{
_CustomerId = value;
}
get
{
return _CustomerId;
}
}
}

Шаг 2: Создание (create) с помощью LINQ

Создать контекст данных

Сначала создается объект ‘datacontext’ с использованием строки подключения.

DataContext objContext = new DataContext(strConnectionString);

Установить данные для вставки

Следующий шаг после создания подключения с помощью объекта ‘DataContext’ – создать объект-сущность «Клиент» и присвоить данные свойству объекта.

clsCustomerEntity objCustomerData = new clsCustomerEntity();
objCustomerData.CustomerCode = txtCustomerCode.Text;
objCustomerData.CustomerName = txtCustomerName.Text;

Сделать обновление в памяти

Затем делается обновление в памяти в самих объектах-сущностях с помощью метода ‘InsertOnSubmit’.

objContext.GetTable<clsCustomerEntity>().InsertOnSubmit(objCustomerData);

Сделать конечную физическую фиксацию

Наконец, делается физическая фиксация в реальной базе данных. Помните, что пока не вызван ‘SubmitChanges()’, данные не зафиксированы окончательно в базе данных.

objContext.SubmitChanges();

Окончательный код

Ниже приведен окончательный собранный код LINQ.

DataContext objContext = new DataContext(strConnectionString);
clsCustomerEntity objCustomerData = new clsCustomerEntity();
objCustomerData.CustomerCode = txtCustomerCode.Text;
objCustomerData.CustomerName = txtCustomerName.Text;
objContext.GetTable<clsCustomerEntity>().InsertOnSubmit(objCustomerData);
objContext.SubmitChanges();

Шаг 3: Обновление (update) с помощью LINQ

Возьмем следующую операцию базы данных, т.е. «Обновить».

Создать контекст данных

Как обычно, сначала надо создать объект ‘datacontext’ с помощью строки подключения, как сказано в шаге «Создать».

DataContext objContext = new DataContext(strConnectionString);

Выбрать объект LINQ пользователь, который надо обновить

Получить путем запроса LINQ объект LINQ, который надо обновить

var MyQuery = from objCustomer in objContext.GetTable<clsCustomerEntity>()
where objCustomer.CustomerId == Convert.ToInt16(txtCustomerId.Text)
select objCustomer;

Наконец, установить новые значения и обновить данные в физической базе данных

Делаются обновления и вызывается ‘SubmitChanges()’ для осуществления окончательного обновления.

clsCustomerEntity objCustomerData = (clsCustomerEntity)MyQuery.First<clsCustomerEntity>();
objCustomerData.CustomerCode = txtCustomerCode.Text;
objCustomerData.CustomerName = txtCustomerName.Text;
objContext.SubmitChanges();

Окончательный код «Обновить на LINQ»

Ниже показано, как выглядит окончательный запрос на обновление на LINQ.

DataContext objContext = new DataContext(strConnectionString);
var MyQuery = from objCustomer in objContext.GetTable<clsCustomerEntity>()
where objCustomer.CustomerId == Convert.ToInt16(txtCustomerId.Text)
select objCustomer;
clsCustomerEntity objCustomerData = (clsCustomerEntity)MyQuery.First<clsCustomerEntity>();
objCustomerData.CustomerCode = txtCustomerCode.Text;
objCustomerData.CustomerName = txtCustomerName.Text;
objContext.SubmitChanges();

Шаг 4: Удаление (Delete) с помощью LINQ

Рассмотрим следующую операцию базы данных – «Удалить».

DeleteOnSubmit

Не разбираются предыдущие шаги, такие как создание контекста данных и выбор объекта LINQ, они объяснены в предыдущем разделе. Чтобы удалить объект из памяти, надо вызвать ‘DeleteOnSubmit()’, а для удаления из конечной базы данных надо использовать ‘SubmitChanges()’.

objContext.GetTable<clsCustomerEntity>().DeleteOnSubmit(objCustomerData);
objContext.SubmitChanges();

Шаг 5: Понятные без пояснений выборка (select) и чтение (read) на LINQ

На последнем шаге объект LINQ выбирается и читается по условию. Ниже приведен фрагмент кода, показывающий, как запустить запрос LINQ и присвоить значение объекта пользовательскому интерфейсу ASP.NET.

DataContext objContext = new DataContext(strConnectionString);

var MyQuery = from objCustomer in objContext.GetTable<clsCustomerEntity>()
where objCustomer.CustomerId == Convert.ToInt16(txtCustomerId.Text)
select objCustomer;

clsCustomerEntity objCustomerData = (clsCustomerEntity)MyQuery.First<clsCustomerEntity>();
txtCustomerCode.Text = objCustomerData.CustomerCode;
txtCustomerName.Text = objCustomerData.CustomerName;