为什么DbConnectionStringBuilder后代打破属性网格绑定如此彻底?



我正在尝试设置一种配置数据库连接的方法。我在GitHub上找到了一个简单的属性网格,将其添加到我的项目中,并将DbConnectionStringBuilder后代绑定到它,它立即破裂。它找到了所有属性的名称和类型,但它似乎实际上并没有链接到对象实例,因此所有属性都显示空值,并且试图编辑它们会导致各种问题。

在向开发人员提交bug报告后,我一无所获,我尝试了大约六个其他属性网格,包括免费和(演示版本)商业产品,并且每一个都有同样的问题!一些连接字符串生成器工作得很好,其他人打破,有时它是不同的网格,但没有一个正确绑定到我使用的整套5,为SQL Server, Postgres,火鸟,MySQL,和一个非常简单的测试用例类我开发只是为了重现这一点。(事实上,我连接到"CSV数据库"的简单测试用例;是唯一一个打破所有规则的人!)

WPF数据绑定对DbConnectionStringBuilder有什么特别奇怪的地方吗?

如果有人想尝试,请复制案例。CSV数据库配置类:

using System;
using System.ComponentModel;
using System.Data.Common;
namespace Repro
{
public class CsvConfigurator : DbConnectionStringBuilder
{
public CsvConfigurator() { }
public CsvConfigurator(string conf)
{
ConnectionString = conf;
}
public string Delimiter
{
get => GetString(nameof(Delimiter));
set => this[nameof(Delimiter)] = value;
}
public bool AutoDetectDelimiter
{
get => GetBool(nameof(AutoDetectDelimiter));
set => this[nameof(AutoDetectDelimiter)] = value;
}
public bool UsesHeader
{
get => GetBool(nameof(UsesHeader));
set => this[nameof(UsesHeader)] = value;
}
public bool UsesQuotes
{
get => GetBool(nameof(UsesQuotes));
set => this[nameof(UsesQuotes)] = value;
}
public char QuoteChar
{
get => GetChar(nameof(QuoteChar), '"');
set => this[nameof(QuoteChar)] = value;
}
public char EscapeChar
{
get => GetChar(nameof(EscapeChar), '\');
set => this[nameof(EscapeChar)] = value;
}
protected string GetString(string key) => TryGetValue(key, out var value) ? (string)value : null;
protected bool GetBool(string key) => TryGetValue(key, out var value) ? Convert.ToBoolean(value) : false;
protected char GetChar(string key, char defaultValue)
{
var result = GetString(key);
return string.IsNullOrEmpty(result) ? defaultValue : result[0];
}
}
}
  1. 创建一个带有属性网格(任何属性网格)的项目。
  2. 实例化上面的类
  3. 将其作为要检查的对象绑定到网格。
  4. 在getter和setter上设置断点,这样你就可以看到数据绑定实际发生的时间。
  5. 看所有没有被束缚的东西。(查看复选框控件以及它们如何处于空状态,尽管所讨论的属性不是bool?类型。)
  6. 编辑一下
  7. 注意断点不被命中。

是的,从DbConnectionStringBuilder派生的类在与属性网格一起使用时具有特殊行为是有原因的。

因为它实现了ICustomTypeDescriptor接口。通常,属性网格使用TypeDescriptor。GetProperties方法,如果实现,默认情况下将推迟到ICustomTypeDescriptor。

它的意思是属性网格将而不是使用编译后的。net/c#属性来表示实例,而不是使用ICustomTypeDescriptor接口的属性,以及自定义的PropertyDescriptor实例。

所以编译后的。net/c#属性根本不会被属性网格使用,只会被"虚拟"。属性由内部DbConnectionStringBuilder代码组成(您可以在这里查看其代码https://github.com/microsoft/referencesource/blob/master/System.Data/System/Data/Common/DbConnectionStringBuilder.cs#L335)。这些"virtual"属性将使用。net编译的属性来构造,但它们的代码不会用于获取或设置它们。

这在某种程度上类似于WPF依赖属性特性,其中。net代码只使用。net类的编译属性,而不使用WPF绑定/XAML引擎(除了WPF使用DependencyProperty.Register代码来定义依赖属性,而不是编译的属性)。

如果你想支持WPF绑定引擎,你可以像这样在你的类中实现INotifyPropertyChanged,例如:

public event PropertyChangedEventHandler PropertyChanged;
// thanks to how DbConnectionStringBuilder is designed,
// we can override this central method
public override object this[string keyword]
{
get => base[keyword];
set
{
object existing = null;
try
{
existing = base[keyword];
}
catch
{
// do nothing
}
if (existing == null)
{
if (value == null)
return;
}
else if (existing.Equals(value))
return;
base[keyword] = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(keyword));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ConnectionString)));
}
}

对于现有的类,比如MySqlConnectionStringBuilder,你什么也做不了(除非用另一个实现ICustomTypeDescriptor的类,用类似于这个DynamicTypeDescriptor的方法包装它们)。并不是每个。net类都可以很好地使用WPF绑定,甚至是标准的Winforms绑定。它是密封的…

最新更新