【问题标题】:Trim String with Dapper.NET使用 Dapper.NET 修剪字符串
【发布时间】:2015-04-10 08:11:38
【问题描述】:

我已经使用Dapper.NET 有一段时间了。我只是想知道是否可以让 Dapper 在将字符串分配给对象的属性时修剪字符串。

我目前在 SQL 中使用LTRIM(RTRIM(fieldname)),和/或在属性设置器中使用value.Trim()

然而,我正在使用一个使用 chars 而不是 varchar 的旧数据库,我想知道是否有办法减少我必须修剪所有内容的时间。

我自己编辑了 dapper 的源代码,但最终破坏了其他映射等,所以放弃了。

只是想知道是否有人有任何可以减少这种开销的建议。 (我可能遗漏了一些非常简单的东西!)

顺便说一下,我正在使用 C# 3.5。

【问题讨论】:

  • 您使用的数据库是什么Legacy name Access?跨基地? ...等
  • 抱歉,它实际上是 MS SQL Server。
  • 那么您是否正在寻找属性设置器的全部内容,但服务器上的 LTRIM(RTRIM(...)) 的性能,这样查询将在服务器端自动修剪每个时间?

标签: c# sql dapper


【解决方案1】:

我不喜欢直接修改 Dapper 的想法。我决定通过创建一个扩展方法来包装 Dapper 并仅反映结果并修剪所有字符串属性来解决这个问题。

public static class DapperExtensions {
    public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) {
        var dapperResult = SqlMapper.Query<T>(cnn, sql, param, transaction, buffered, commandTimeout, commandType);
        var result = TrimStrings(dapperResult.ToList());
        return result;
    }

    static IEnumerable<T> TrimStrings<T>(IList<T> objects) {
        //todo: create an Attribute that can designate that a property shouldn't be trimmed if we need it
        var publicInstanceStringProperties = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType == typeof (string) && x.CanRead &&  x.CanWrite);
        foreach (var prop in publicInstanceStringProperties) {
            foreach (var obj in objects) {
                var value = (string) prop.GetValue(obj);
                var trimmedValue = value.SafeTrim();
                prop.SetValue(obj, trimmedValue);
            }
        }
        return objects;
    }

    static string SafeTrim(this string source) {
        if (source == null) {
            return null;
        }
        return source.Trim();
    }
}

对我的解决方案至关重要(因为我想使用与 Dapper 相同的名称)是扩展方法解析的工作原理,您可以阅读有关 here 的信息。

【讨论】:

  • 我不敢相信我真的没有想到这个!实际上,我也围绕它实现了自己的包装器,但还有其他用途。我没想到在那里做字符串修剪!
【解决方案2】:

我假设您想要一种更自然的方式来通过您的 POCO 暗示 LTRIM(RTRIM()) 函数,这样您就不需要在每次想要返回时手动输入 LTRIM(RTRIM())字段,为您提供 SQL 性能,无需手动重复劳动。

我有两个选择:

在 Dapper-Dot-Net 方面:

您可以在查询函数阶段添加一些代码,对原始 SQL 查询执行替换算法。

  • 如果查询中有多个表,我会认为这超出了范围
  • 否则,我相信这可以很容易地完成,而不会破坏 dapper 引擎的其余部分。

流程:

  • 首先对你的表名执行一个无知的 IndexOf,忽略大小写,这样你就可以快速传递,如果不是。
  • 如果找到表名,则对查询执行一些更可靠的分析,确保您找到的表名实际上是查询中的表名。在这里,您还可以确保查询中只有一个表且没有连接。
  • 如果 select 上有 *,则可以根据您的 poco 定义将其扩展为字段名称
  • 所以现在您只需在您的选择阵容中有字段
  • 对于所有字段,在选择队列中,找到 poco 定义中的名称,其中有 [Trim] 属性,将字段名称替换为 LTRIM(RTRIM({FieldName})) 为 [{FieldName} ]

在 SQL 端:

如果您有权在服务器上创建视图,那么您可以编写一个存储过程来创建/更改(更新)一组视图,这些视图为 char 字段公开 varchar 接口。

批量更新 - 每次架构更改时运行: 1.遍历所有用户表 2.创建或更新对应的View

更新表视图(表名): 1.如果没有char字段则退出 2. 使用 cast(LTRIM(RTRIM(..)) as varchar(CHAR_FIELD_SIZE) 自动创建视图

【讨论】:

  • 感谢托德的建议。一定会试一试的。我们的很多查询确实使用多个表。但是,如果我们使用可行的视图。我不确定这会对它的性能产生多大影响。会尝试看看效果如何!
  • RE:视图 - 通常视图可以提高性能。物化视图允许您在视图上创建索引。确保在架构更改时“刷新”视图。某些 ORM 可能无法通过视图回写,尤其是在缺少 ID 的情况下。
【解决方案3】:

马特,

这可以很容易地完成。我做了这个改变来清理 SQL 字符空间。我已经对其进行了测试,如果由于更改而变慢,我的代码没有任何迹象。

首先确保备份现有的源代码,以便在需要时更容易恢复。

下一步创建以下方法:

public static string ReadString(object value) /*** CUSTOM CODE ***/
{
    if (value == null || value is DBNull) return null; 
    return value.ToString().Trim();
}

我总是用 /* CUSTOM CODE */ 标记我的所有代码更改,以便我以后可以轻松找到我的更改

下一步找到以下方法:

public static void SetTypeMap(Type type, ITypeMap map)

现在在该方法中找到以下行:

if (memberType == typeof (char) || memberType == typeof (char?))
{
    il.EmitCall(OpCodes.Call, typeof (SqlMapper).GetMethod(
        memberType == typeof (char) ? "ReadChar" : "ReadNullableChar",
        BindingFlags.Static | BindingFlags.Public), null);
        // stack is now [target][target][typed-value]
}
else

并修改如下:

if (memberType == typeof (char) || memberType == typeof (char?))
{
    il.EmitCall(OpCodes.Call, typeof (SqlMapper).GetMethod(
        memberType == typeof (char) ? "ReadChar" : "ReadNullableChar",
        BindingFlags.Static | BindingFlags.Public), null);
        // stack is now [target][target][typed-value]
}
else if (memberType == typeof(string)) /*** CUSTOM CODE START ***/
{
    il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ReadString", BindingFlags.Static | BindingFlags.Public), null);
    // stack is now [target][target][typed-value]
}    /*** CUSTOM CODE END ***/
else

编译即可开始使用

【讨论】:

  • 这太棒了。我也尝试编辑 SetTypeMap,但我真的不知道我在用 Emit Calls 做什么,而且我也不知道在哪里处理字符串字段。我不敢相信你所要做的就是添加另一个如果!
猜你喜欢
  • 2011-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多