65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (16投票s)

2009 年 7 月 1 日

CPOL

5分钟阅读

viewsIcon

133100

downloadIcon

1389

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

目录

引言

在本文中,我们将从一个基本的 LINQ to SQL 示例开始,然后学习如何使用 EntityrefEntitySet 来实现一对多和一对一关系。我附带了一个实际演示此功能的源代码。

之前的 LINQ、Silverlight、WCF、WPF 和 WWF 文章

如果您不熟悉 .NET 3.5 的基本概念,您可能需要参考我之前的一些文章。

  1. 本文介绍了一个完整的基于 LINQ 的三层实现:https://codeproject.org.cn/KB/aspnet/SaltAndPepper.aspx
  2. 涵盖从简单到高级概念的 WCF FAQ 系列:https://codeproject.org.cn/KB/aspnet/WCF.aspx
  3. 涵盖布局、动画和绑定的 WPF 和 Silverlight FAQ 系列:https://codeproject.org.cn/KB/WPF/WPFSilverLight.aspx
  4. 详细介绍状态机和顺序工作流的 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 数据类型获取实体集合。这是通过 DataContextGetTable 函数完成的。

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 允许您使用 EntitySetEntityRef 来定义关系。为了理解如何使用 LINQ 定义关系,让我们考虑下面的示例,其中一个客户可以拥有多个地址,每个地址都有电话详细信息。换句话说,客户和地址之间是一对多关系,而地址和电话之间是一对一关系。

要定义客户类和地址类之间的一对多关系,我们需要使用 EntitySet 属性。要定义地址类和电话类之间的一对一关系,我们需要使用 EntityRef 属性。

注意:您需要为每个实体类定义主键属性,否则映射关系将不起作用。

下面是客户类的类实体代码片段,它显示了如何使用 EntitySet 定义与地址类的一对多关系。关联使用 Association 属性定义。Association 属性有三个重要属性:storagethiskeyotherkeystorage 定义存储地址对象的私有变量的名称。目前它是 _CustomerAddressesThisKeyOtherKey 定义哪个属性将定义链接;在此实例中,它是 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 示例以及带有 entityrefentityset 的关系 LINQ 示例。

如需进一步阅读,请观看以下面试准备视频和分步视频系列。

© . All rights reserved.