使用LINQ to SQL实现一对多和一对一关系






4.17/5 (16投票s)
使用 LINQ to SQL 实现一对多和一对一关系。
目录
引言
在本文中,我们将从一个基本的 LINQ to SQL 示例开始,然后学习如何使用 Entityref
和 EntitySet
来实现一对多和一对一关系。我附带了一个实际演示此功能的源代码。
之前的 LINQ、Silverlight、WCF、WPF 和 WWF 文章
如果您不熟悉 .NET 3.5 的基本概念,您可能需要参考我之前的一些文章。
- 本文介绍了一个完整的基于 LINQ 的三层实现:https://codeproject.org.cn/KB/aspnet/SaltAndPepper.aspx
- 涵盖从简单到高级概念的 WCF FAQ 系列:https://codeproject.org.cn/KB/aspnet/WCF.aspx
- 涵盖布局、动画和绑定的 WPF 和 Silverlight FAQ 系列:https://codeproject.org.cn/KB/WPF/WPFSilverLight.aspx
- 详细介绍状态机和顺序工作流的 Windows 工作流 FAQ 系列:https://codeproject.org.cn/KB/WF/WWF.aspx
最简单的 LINQ to SQL 示例
让我们先从一个简单的 LINQ to SQL 示例开始,然后再尝试理解如何在 LINQ 实体中建立关系。
步骤 1:使用 LINQ 定义实体类
当我们使用 3 层或 N 层等分层方法设计项目时,我们需要创建业务类和对象。例如,下面是一个映射到 country 表的简单类。您可以看到类属性如何以某种方式映射到表。这类类称为实体类。
在 LINQ 中,我们需要先使用属性映射来定义这些实体类。您需要导入 System.Data.Linq.Mapping
命名空间来获取映射属性。下面是一个代码片段,显示了 Table
属性如何将类映射到数据库表名 Customer,以及 Column
属性如何帮助将属性映射到表列。
[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;
}
}
}
下面是实体类与 customer 表结构映射的更复杂的图形化视图
步骤 2:使用 DataContext 将表数据绑定到实体对象
第二步是使用 LINQ 的 DataContext
对象来填充您的实体对象。DataContext
在数据库对象和您 LINQ 实体映射类之间充当中介。
首先,创建一个 DataContext
对象并使用 SQL 连接字符串创建一个活动连接。
DataContext objContext = new DataContext(strConnectionString);
第二步是使用 Table
数据类型获取实体集合。这是通过 DataContext
的 GetTable
函数完成的。
Table<clsCustomerEntity> objTable = objContext.GetTable<clsCustomerEntity>();
一旦我们获得了所有表集合中的数据,就可以遍历表集合并显示记录了。
foreach (clsCustomerEntity objCustomer in objTable)
{
Response.Write(objCustomer.CustomerName + "<br>");
}
您可以从本文附带的下载中获取上述源代码。
使用 Set 和 Get 封装的 LINQ 类
在前面的示例中,我们将实体类属性公开为公共属性,这违反了封装的基本规则。您可以定义 setter 和 getter 函数来封装私有属性。
[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;
}
}
}
一对多和一对一关系
LINQ 允许您使用 EntitySet
和 EntityRef
来定义关系。为了理解如何使用 LINQ 定义关系,让我们考虑下面的示例,其中一个客户可以拥有多个地址,每个地址都有电话详细信息。换句话说,客户和地址之间是一对多关系,而地址和电话之间是一对一关系。
要定义客户类和地址类之间的一对多关系,我们需要使用 EntitySet
属性。要定义地址类和电话类之间的一对一关系,我们需要使用 EntityRef
属性。
注意:您需要为每个实体类定义主键属性,否则映射关系将不起作用。
下面是客户类的类实体代码片段,它显示了如何使用 EntitySet
定义与地址类的一对多关系。关联使用 Association
属性定义。Association
属性有三个重要属性:storage
、thiskey
和 otherkey
。storage
定义存储地址对象的私有变量的名称。目前它是 _CustomerAddresses
。ThisKey
和 OtherKey
定义哪个属性将定义链接;在此实例中,它是 CustomerId
。换句话说,Customer
类和 Address
类都将有一个通用的 CustomerId
属性。ThisKey
定义 Customer
类的属性名称,而 OtherKey
定义 addresses 类的属性。
[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
定义关系的 Snippet。所有其他属性都相同,只是我们需要使用 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);
第二步是执行查询。请注意,我们只为 customer 类执行查询。LINQ 引擎确保所有子表数据都根据实体类中定义的关系统提取并放置。
var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;
最后,我们遍历 customer,然后遍历相应的 addresses 对象,并根据 phone 对象显示电话详细信息。
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>");
}
}
输出如下图所示。每个客户都有多个地址,每个地址都有一个电话对象。
源代码
我们还附带了一个包含 customer、address 和 phone 表的源文件。示例代码演示了一个简单的 LINQ 示例、带有属性的 LINQ 示例以及带有 entityref
和 entityset
的关系 LINQ 示例。
如需进一步阅读,请观看以下面试准备视频和分步视频系列。