NpgSQLdataReader GetOrdinal 抛出异常..有什么办法吗?

发布于 2024-08-21 17:44:46 字数 1626 浏览 1 评论 0原文

我围绕 NpgSQL 为我在项目的 DAL 中通常使用的一系列方法构建了一个包装器。我通常使用其中两个直接从 DataReader 填充 DTO。通常在填充帮助器方法中,我将实例化 DTO 并迭代将 Datareader 的数据映射到相应属性的属性。 fill方法大部分时候都是生成的。

由于我允许许多属性为空或使用 DTO 的默认值,因此在填写属性之前,我使用了一种方法来检查 dataReader 的数据对于该属性是否有效。所以我将有一个 IsValidString("fieldname") 和一个 DRGetString("fieldname") 方法,如下所示:

public bool IsValidString(string fieldName)
{
        if (data.GetOrdinal(fieldName) != -1
            && !data.IsDBNull(data.GetOrdinal(fieldName)))
            return true;
        else
            return false;
}

public string DRGetString(string fieldName)
{
        return data.GetString(data.GetOrdinal(fieldName));
}

我的 fill 方法被委派到执行查询的任何方法,看起来像:

public static object FillObject(DataParse<PostgreSQLDBDataParse> dataParser)
{
     TipoFase obj = new TipoFase();   

     if (dataParser.IsValidInt32("T_TipoFase"))
        obj.T_TipoFase = dataParser.DRGetInt32("T_TipoFase");

     if (dataParser.IsValidString("NM_TipoFase"))
        obj.NM_TipoFase = dataParser.DRGetString("NM_TipoFase");

            //...rest of the properties .. this is usually autogenerated by a T4 template

     return obj;
}

这在 NpgSQL 2.02 之前工作得很好。 。 。当调用 GetOrdinal 方法时,如果该字段在 dataReader 中不存在,我只会返回 -1。很容易在 IsValidString() 中返回 false 并简单地跳到下一个属性。检查不存在的字段对性能的影响实际上可以忽略不计。

不幸的是,对 NpgSQL 的更改使得 GetOrdinal 在字段不存在时抛出异常。我有一个简单的解决方法,将代码包装在 try /catch 中,并在 catch 中抛出 false。但我可以感觉到性能受到影响,尤其是当我进入调试模式时。填写一长串清单需要几分钟的时间。

据说,NpgSQL 有一个可以添加到连接字符串(兼容性)的参数,以支持此方法的向后兼容性,但我从未让它正常工作(由于连接字符串格式错误,我总是收到异常)。无论如何,我正在寻找更好的解决方法的建议。有没有更好的方法来从数据读取器填充对象,甚至以某种方式解决异常问题?

I built a wrapper around NpgSQL for a bunch of the methods I usually use in my projects' DAL. Two of them, I usually use to fill DTOs straight from a DataReader. Usually in a fill helper method, i'll instanciate the DTO and iterate through the properties mapping the Datareader's data to the corresponding property. The fill method is generated most of the time.

Since i allow many of the properties to be null or use the DTO's default values, I've used a method to check if the dataReader's data is valid for the property before filling in the prperty. So i'll have a IsValidString("fieldname") and a DRGetString("fieldname") methods, like so:

public bool IsValidString(string fieldName)
{
        if (data.GetOrdinal(fieldName) != -1
            && !data.IsDBNull(data.GetOrdinal(fieldName)))
            return true;
        else
            return false;
}

public string DRGetString(string fieldName)
{
        return data.GetString(data.GetOrdinal(fieldName));
}

My fill method is delagated to whatever method executed the query and looks like:

public static object FillObject(DataParse<PostgreSQLDBDataParse> dataParser)
{
     TipoFase obj = new TipoFase();   

     if (dataParser.IsValidInt32("T_TipoFase"))
        obj.T_TipoFase = dataParser.DRGetInt32("T_TipoFase");

     if (dataParser.IsValidString("NM_TipoFase"))
        obj.NM_TipoFase = dataParser.DRGetString("NM_TipoFase");

            //...rest of the properties .. this is usually autogenerated by a T4 template

     return obj;
}

This was working fine and dandy in NpgSQL pre 2.02. . When the GetOrdinal method was called, and if the field was inexistent in the dataReader, I'd simply get a -1 returned. Easy to return false in IsValidString() and simply skip to the next property. The performace hit from checking inexistent fields was practically neglectable.

Unfortunately, changes to NpgSQL make GetOrdinal throw an exception when the field doesn't exist. I have a simple workaround in which I wrap the code in a try /catch and throw false within the catch. But I can feel the hit in performance, especially when I go in to debug mode. Filling in a long list takes minutes.

Suposedly, NpgSQL has a parameter that can be added to the connection string (Compatability) to support backward compatabilty for this method, but I've never got that to work correctly (I always get an exception because of a mal formed connection string). Anyway, I'm looking for suggestions for better workarounds. Any better way to fill in the object from the datareader or even somehow work around the exception problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我做我的改变 2024-08-28 17:44:46

我已经为我的问题创建了一个解决方案,不需要很大的改变,并且呈现出有趣的性能(或者看起来是这样)。可能只是一个新的解析库/包装器。

基本上,我将遍历 dataReader 的字段,并将每个字段复制到集合(在我的例子中是列表)。然后我将检查有效数据,如果认为有效,我会将数据复制到对象的属性中。

所以我将:

public class ParserFields
{
    public string FieldName { get; set; }
    public Type FieldType { get; set; }
    public object Data { get; set; }
}

我将使用以下方式填充对象:

public static object FillObjectHashed(DataParse<PostgreSQLDBDataParse> dataParser)
    {
        //The the Field list with field type and data
        List<ParserFields> pflist = dataParser.GetReaderFieldList(); 

        //create resulting object instance
        CandidatoExtendido obj = new CandidatoExtendido();

        //check for existing field and valid data and create object
        ParserFields pfdt = pflist.Find(objt => objt.FieldName == "NS_Candidato");
        if (pfdt != null && pfdt.FieldType == typeof(int) && pfdt.Data.ToString() != String.Empty)
            obj.NS_Candidato = (int)pfdt.Data;

        pfdt = pflist.Find(objt => objt.FieldName == "NM_Candidato");
        if (pfdt != null && pfdt.FieldType == typeof(string) && pfdt.Data.ToString() != String.Empty)
            obj.NM_Candidato = (string)pfdt.Data;

        pfdt = pflist.Find(objt => objt.FieldName == "Z_Nasc");
        if (pfdt != null && pfdt.FieldType == typeof(DateTime) && pfdt.Data.ToString() != String.Empty)
            obj.Z_Nasc = (DateTime)pfdt.Data;

        //...

        return obj;
    }

我计时了我的变化,以查看差异。进行了搜索,返回了 612 个结果。首先,我也查询了数据库两次,以考虑查询的第一次运行以及与缓存相关的后续差异(这非常重要)。我的 FillObject 方法只是创建了要添加到结果列表中的所需对象的新实例。

  • 对对象实例列表的第一个查询:2896K 勾选
  • 第二个查询(与第一个相同):1141K 勾选

然后我尝试使用之前的填充对象

  • 到所需对象的列表,填充为返回数据或默认值,检查所有对象属性:3323K 勾选
  • 到所需对象列表,仅检查搜索中返回的对象属性:1127K 勾选
  • 到所需对象列表对象,使用查找列表,仅检查返回的字段:1097K 勾选
  • 要列出所需对象,使用查找列表,检查所有字段(减去一些嵌套属性):1107K 我使用的原始代码消耗的刻度数

比使用仅限于所需字段的方法时多了近 3 倍。令人期待的事情扼杀了它。

使用 fillobject 方法的新代码,与仅检查所需字段相比,检查不存在字段的开销最小。

至少目前看来,这似乎很有效。可能会尝试寻找一些优化。
任何建议将不胜感激!

I have created a solution to my problem, that doesn't require great changes, and presents interesting performance (or so it seems). Might just be a new parsing library / wrapper.

Basicly, I'll iterate through the dataReader's fields, and copy each to a Collection (in my case a List). Then I'll check for valid data and if considered valid, I'll copy the data to the object's property.

So I'll have:

public class ParserFields
{
    public string FieldName { get; set; }
    public Type FieldType { get; set; }
    public object Data { get; set; }
}

and I'll fill the object using:

public static object FillObjectHashed(DataParse<PostgreSQLDBDataParse> dataParser)
    {
        //The the Field list with field type and data
        List<ParserFields> pflist = dataParser.GetReaderFieldList(); 

        //create resulting object instance
        CandidatoExtendido obj = new CandidatoExtendido();

        //check for existing field and valid data and create object
        ParserFields pfdt = pflist.Find(objt => objt.FieldName == "NS_Candidato");
        if (pfdt != null && pfdt.FieldType == typeof(int) && pfdt.Data.ToString() != String.Empty)
            obj.NS_Candidato = (int)pfdt.Data;

        pfdt = pflist.Find(objt => objt.FieldName == "NM_Candidato");
        if (pfdt != null && pfdt.FieldType == typeof(string) && pfdt.Data.ToString() != String.Empty)
            obj.NM_Candidato = (string)pfdt.Data;

        pfdt = pflist.Find(objt => objt.FieldName == "Z_Nasc");
        if (pfdt != null && pfdt.FieldType == typeof(DateTime) && pfdt.Data.ToString() != String.Empty)
            obj.Z_Nasc = (DateTime)pfdt.Data;

        //...

        return obj;
    }

I timed my variations, to see the diferences. Did a search that returned 612 results. First I queried the database twice too take in to account the first run of the query and the subsequent diferences related to caching ( and that where quite significant). My FillObject method simply created a new instance of the desired object to be added to the results list.

  • 1st query to List of object's instances : 2896K ticks
  • 2nd query (same as first) : 1141K ticks

Then I tried using the previous fill objects

  • To List of desired object, filled with return data or defaults, checking all of the objects properties: 3323K ticks
  • To List of desired objects, checking only the object's properties returned in the search: 1127K ticks
  • To list of desired objects, using lookup list, checking only the returned fields: 1097K ticks
  • To list of desired objects, using lookup list, checking all of the fields (minus a few nested properties): 1107K ticks

The original code i was using was consuming nearly 3 times more ticks than when using a method limited to the desired fields. The excpetions where killing it.

With the new code for the fillobject method, the overhead for checking inexistente fileds mas minimal compared to just checking for the desired fields.

This seems to work nice, for now at least. Might try looking for a couple of optimizations.
Any sugestion will be appreciated!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文