如何使用问号通过lambda / LINQ选择通配符数据表?

时间:2019-03-05 00:22:57

标签: c# linq lambda

我有一个C#数据表(// In ViewDidLoad NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) @objc func keyboardDidChange(notification: Notification) { let userInfo = notification.userInfo! as [AnyHashable: Any] let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part. // Prevents iPad undocked keyboard. guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else { bottomLayoutConstraint.constant = 0 return } UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!) UIView.animate(withDuration: animationDuration.doubleValue) { self.view.layoutIfNeeded() // Do additional tasks such as scrolling in a UICollectionView } } ),看起来像这样:

enter image description here

我有以下内存_dtOptions

select

我使用DataRow[] matchingRows = _dtOptions.Select("OptionID Like 'M00F????'"); 而不是?的原因是因为我只想匹配以*开头的8个字符的记录。

问题:执行该行代码后M00F为空。正如您在屏幕截图中看到的那样,它应该与很多记录匹配。

注意:当我对基础matchingRows数据库运行此查询时,确实得到了预期的结果:Access

我在做什么错了?

2 个答案:

答案 0 :(得分:1)

请尝试以下操作:

DataRow[] matchingRows = _dtOptions.AsEnumerable()
    .Where(x => x.Field<string>("OptionID").Length == 8 &&
        x.Field<string>("OptionID").StartsWith("M00F"))
    .ToArray();

答案 1 :(得分:1)

分开您的担忧

出现问题是因为您将数据的存储方式(DataTable)与您要处理数据的方式(LINQ语句)以及可能要与结果进行处理的方式(在屏幕上显示)混合在一起?)

正确的方法是将这些问题分开:将数据在内部的保存方式与处理方式分开,并处理结果。

在您的示例中,您的数据是从DataTable检索的,然后进行一些处理,并且可能会对结果进行某些处理。

如果将来您决定从列表,数据库或CSV文件或从Internet检索相似的数据,则您既不想更改处理部分,也不想更改执行操作的部分结果。

类似地,如果您重新排列DataTable中的列,或者以不同的方式命名它们,则您不希望弄乱处理过程。想一想您必须检查的所有代码和所有单元测试,仅因为重命名了一个列!

分开您的担忧有一些好处

  • 代码将更容易理解:每个关注点都具有一个明确定义的功能。您无需使用很多单词即可定义该功能。
  • 代码e会更容易更改:例如,如果您的DataTable被数据库替换,更改次数将减少;
  • 更易于测试:您可以使用列表作为输入来测试LINQ语句;无需为测试创建数据集
  • 更好的可重用性:您可以重用DataTable提取数据以用于其他目的。您可以将LINQ语句重新用于其他输入源。

因此,让我们分开关注吧!

回到您的问题

首先,您需要一个类来表示保存在DataTable中的数据。显然,这是IEnumerable每一行中保存的DataTable数据序列。像这样:

class MyData
{
    public string OptionId {get; set;}
    public string CatId {get; set;}
    ...
}

您还可以创建一个代表a DataRow containing one object of MyData的类和一个代表a DataTable that contains a sequence of my DataRows的类。

我个人认为这些类不会增加很多功能,因此我将为DataRowDataTable写一些类似LINQ的扩展函数,这些函数会将您的表转换为一个可枚举的MyData。参见Extension Methods demystified

// Convert a DataRow to a MyData:
public static MyData ToMyData(this DataRow row)
{
    // Todo: check for null row
    return new MyData
    {
        OptionId = row... // TODO implement
        ...
    }
}

// Convert a sequence of DataRows to a sequence of MyData
public static IEnumerable<MyData> ToMyData(this IEnumerable<DataRow> rows)
{
    // TODO: check for null rows
    return rows.Select(row => row.ToMyData());
}

// Convert a DataTable to a sequence of MyData:
public static IEnumerable<MyData> ToMyData(this DataTable table)
{
     // TODO: check for null table
     return table.Rows.Cast<DataRow>().ToMyData();
}

因此,经过三个单行函数,您可以将DataTable转换为MyData序列,您可以使用LINQ:

DataTable table = ...
IEnumerable<MyData> myItems = table.ToMyData();

您只希望表中的MyData对象具有OptionId,长度为8,并且以字符串“ M00F”开头。

因为我消除了顾虑,所以实现非常简单:

var matchingData = myItems.Where(myItem => myItem.OptionId.Length == 8
                                        && myItem.OptionId.StartsWith("M00F"));

每个myItem都是MyData的对象。

如果您打算更频繁地使用MatchingData这个概念,或者要记住,将来您可能想更改匹配的数据,例如以“ M11F”开头的数据,因此如果希望使此需求更好地可更改,可重用,可测试(无论如何,您现在就知道了演练),使其成为MyData的扩展功能:

public static IEnumerable<MyData> WhereMatches(this IEnumerable<MyData> source)
{
    // TODO: check for null source
    return source.Where(item => item.OptionId.Length == 8
    && item.OptionId.StartsWith("M00F"));
}

看看测试这个有多容易!无需测试DataTables等。如果您的信息来自数据库,则此功能也将起作用。如果您需要匹配“ M11F”,只需进行少量更改

您在一份声明中的完整要求:

DataTable table = ...
var matchinData = table.ToMyData().WhereMatches();

一些旁注:

  • 如果要确定不能将不正确的数据集放入ToMyData中,请考虑创建特殊的MyDataTable和MyDataRow,它们是从DataTable和DataRow派生的。此类隐藏了DataTable的组织方式。除此之外,还要确保不要将格式错误的DataTable放入函数中。但是,在这种情况下,代码是如此之小,似乎几乎不会添加任何有用的功能。
  • 考虑创建函数以进行转换:将MyData序列转换为DataTable,或将DataRows序列转换为可将行添加到现有DataTable中。