Часто задаваемые вопросы о LINQ - часть вторая - Простой пример отношений 1-1 и 1-много.
ОГЛАВЛЕНИЕ
Простой пример отношений 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>");
}
}
Вывод выглядит так, как показано ниже. Каждый клиент имеет несколько адресов, и каждый адрес имеет объект телефона.