LINQ to SQL 支持绑定到常见控件,例如网格控件。 具体而言,LINQ to SQL 将定义用于绑定到数据网格和处理主-从绑定的基本模式,这两者都与显示和更新有关。
基本原理
LINQ to SQL 将 LINQ 查询转换为 SQL,以便在数据库上执行。 所得结果为强类型化的 IEnumerable
。 由于这些对象是普通的公共语言运行时 (CLR) 对象,因此可以使用普通对象数据绑定来显示结果。 另一方面,更改作(插入、更新和删除)需要其他步骤。
操作
隐式绑定到 Windows 窗体控件是通过实现 IListSource 完成的。 数据源泛型 Table<TEntity> (Table<T>
在 C# 或 Table(Of T)
Visual Basic 中)和泛型 DataQuery
已更新以实现 IListSource。 用户界面(UI)数据绑定引擎(Windows 窗体和 Windows Presentation Foundation)都测试其数据源是否实现 IListSource。 因此,将查询的直接影响结果写入控件的数据源的操作会隐式调用 LINQ to SQL 集合生成,如以下示例所示:
DataGrid dataGrid1 = new DataGrid();
DataGrid dataGrid2 = new DataGrid();
DataGrid dataGrid3 = new DataGrid();
var custQuery =
from cust in db.Customers
select cust;
dataGrid1.DataSource = custQuery;
dataGrid2.DataSource = custQuery;
dataGrid2.DataMember = "Orders";
BindingSource bs = new BindingSource();
bs.DataSource = custQuery;
dataGrid3.DataSource = bs;
Dim dataGrid1 As New DataGrid()
Dim dataGrid2 As New DataGrid()
Dim dataGrid3 As New DataGrid()
Dim custQuery = _
From cust In db.Customers _
Select cust
dataGrid1.DataSource = custQuery
dataGrid2.DataSource = custQuery
dataGrid2.DataMember = "Orders"
Dim bs = _
New BindingSource()
bs.DataSource = custQuery
dataGrid3.DataSource = bs
Windows Presentation Foundation 也是如此:
ListView listView1 = new ListView();
var custQuery2 =
from cust in db.Customers
select cust;
ListViewItem ItemsSource = new ListViewItem();
ItemsSource = (ListViewItem)custQuery2;
Dim listView1 As New ListView()
Dim custQuery2 = _
From cust In db.Customers _
Select cust
Dim ItemsSource As New ListViewItem
ItemsSource = custQuery2
泛型 Table<TEntity> 和泛型 DataQuery
在 GetList 中实现了集合代系。
IListSource 实现
LINQ to SQL 在两个位置实现 IListSource。
数据源为 Table<TEntity>:LINQ to SQL 浏览表以填充在该表中保留引用的
DataBindingList
集合。数据源为 IQueryable<T>。 有两种情况:
如果 LINQ to SQL 从 Table<TEntity> 中找到基础 IQueryable<T>,则源允许编辑,这种情形与第一条要点中的情形相同。
如果 LINQ to SQL 找不到基础Table<TEntity>,则源不允许编辑(例如
groupby
)。 LINQ to SQL 浏览查询以填充泛型SortableBindingList
,这是一个简单的 BindingList<T>,它为给定属性的 T 实体实现排序功能。
专用集合
对于本文档前面所述的许多功能,BindingList<T> 已被专门适用于一些不同的类。 这些类是泛型 SortableBindingList
类和泛型 DataBindingList
类。 这两种类都声明为内部类。
泛型 SortableBindingList
此类继承自 BindingList<T>,是 BindingList<T> 的可排序版本。 排序是内存中解决方案,永远不会联系数据库本身。
BindingList<T>实现IBindingList,但默认情况下不支持排序。 但是,BindingList<T>使用虚拟IBindingList方法实现。 您可以很容易地重写这些方法。 泛型SortableBindingList
覆盖SupportsSortingCore、SortPropertyCore、SortDirectionCore和ApplySortCore。
ApplySortCore
由 ApplySort 调用,用于对给定属性的 T 项列表进行排序。
如果该属性不属于 T,则会引发异常。
为了实现排序,LINQ to SQL 将创建一个泛型 SortableBindingList.PropertyComparer
类,此类继承自泛型 IComparer.Compare,并为给定类型 T 实现一个默认比较器,并实现一个 PropertyDescriptor
和一个方向。 此类可以动态地创建 T 的 Comparer
,其中 T 是 PropertyType
的 PropertyDescriptor
。 然后,从静态泛型 Comparer
中检索默认比较器。 使用反射获取默认实例。
泛型 SortableBindingList
也是 DataBindingList
的基类。 泛型 SortableBindingList
提供两种可用于开始或停止跟踪项添加/删除的虚拟方法。 这两种方法可用于排序等基本功能,但实际上将由泛型 DataBindingList
等上层类实现。
泛型 DataBindingList
此类继承自泛型 SortableBindingLIst
。 泛型 DataBindingList
保留对用于在最初填充集合的泛型 Table
的基础泛型 IQueryable
的引用。 泛型 DatabindingList
通过重写 InsertItem
() 和 RemoveItem
() 将对项添加/移除操作的跟踪添加到集合。 它还实现了抽象暂停/恢复跟踪功能,使跟踪具有条件性。 此功能使泛型 DataBindingList
利用父类跟踪功能的所有多态用法。
绑定到 EntitySet
绑定到EntitySet
是一种特殊情况,因为EntitySet
已经是一个实现IBindingList的集合。 LINQ to SQL 添加了排序和取消 (ICancelAddNew) 支持。 类 EntitySet
使用内部列表来存储实体。 此列表是基于泛型数组(泛型 ItemList
类)的低级别集合。
添加排序功能
数组提供一个可与 T 的 Array.Sort()
一起使用的排序方法 (Comparer
)。LINQ to SQL 使用本主题前面部分介绍的泛型 SortableBindingList.PropertyComparer
类来获取属性的此 Comparer
以及排序方向。 将 ApplySort
方法添加到泛型 ItemList
以调用此功能。
在 EntitySet
端,您现在必须声明排序支持:
SupportsSorting 返回
true
。ApplySort 调用
entities.ApplySort()
,然后OnListChanged()
。SortDirection 和 SortProperty 属性公开存储在本地成员中的当前排序定义。
使用 System.Windows.Forms.BindingSource 并将 EntitySet<TEntity> 绑定到 System.Windows.Forms.BindingSource.DataSource 时,必须调用 EntitySet<TEntity>.GetNewBindingList 来更新 BindingSource.List。
如果使用 System.Windows.Forms.BindingSource,设置 BindingSource.DataMember 属性,并将 BindingSource.DataSource 设置为具有名为 BindingSource.DataMember 的属性(该属性公开 EntitySet<TEntity>)的类,则无需调用 EntitySet<TEntity>.GetNewBindingList 来更新 BindingSource.List,但将失去排序功能。
缓存
LINQ to SQL 查询实现 GetList。 当 Windows 窗体 BindingSource 类满足此接口时,它会为单个连接调用 GetList() 三次。 为了解决此问题,LINQ to SQL 实现每个实例的缓存来存储和始终返回相同的生成集合。
取消
IBindingList
AddNew定义控件用于从绑定集合创建新项的方法。 当最后一个可见行的标题中包含星形时,该DataGridView
控件能够很好地展示这一功能。 星形符号显示您可以添加一个新项目。
除此功能外,集合还可以实现 ICancelAddNew。 此功能允许控件取消或验证新编辑的项目是否已验证。
在所有 LINQ to SQL 数据绑定集合(泛型 ICancelAddNew 和泛型 SortableBindingList
)中实现 EntitySet
。 在这两个实现中,代码执行如下作:
允许插入项,然后从集合中删除项。
只要 UI 不提交版本,就不会跟踪更改。
只要版本被取消(CancelNew)就不会跟踪更改。
允许在提交 (EndNew) 编辑内容时进行跟踪。
如果新项不是来自 AddNew,则集合的行为正常。
故障排除
本部分将调用多个有助于对 LINQ to SQL 数据绑定应用程序进行故障排除的项。
必须使用属性;仅使用字段是不够的。 Windows 窗体需要这种用法。
默认情况下,
image
和varbinary
timestamp
数据库类型映射到字节数组。 由于ToString()
在此方案中不受支持,因此无法显示这些对象。映射到主键的类成员具有 setter,但 LINQ to SQL 不支持对象标识更改。 因此,无法在数据库中更新映射中使用的主/唯一键。 在调用 SubmitChanges时,网格中的更改会导致异常。
如果实体绑定到两个单独的网格(例如,一个主网格和另一个详细信息),则主网格中的实体
Delete
不会传播到详细信息网格。