none
Problem accessing OleDbDataReader from another thread RRS feed

  • Question

  • Hi,

    I've read a few forum posts and it looks like this might be the case where i cant use the reader from another thread other than the one which created it but will post my issue anyway.

    Host App has a number of plugins, one of them being a ms Access plugin which  creates the reader connection, then calls an async "Process" method which passes in the reader to process data.

    As soon as i try and access a reader field property it throws the following error.

    Unable to cast COM object of type 'System.__ComObject' to interface type 'IRowset'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{0C733A7C-2A1C-11CE-ADE5-00AA0044773D}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).


    What are my options?

    Friday, December 4, 2015 12:46 AM

Answers

  • Thanks for your reply, i can see it works ok in your example which unfortunately causes me to refactor a heap of code to make this work in my app as my plugin interface would me require to change all my plugin implementations and i dont understand why using:

    using (DbDataReader rdr = await _cmd.ExecuteReaderAsync())
        {
            await ProcessAsync(rdr);
        }

    as compared to:

    using (var rdr = GetReader())
                {
                    using (var t = Task.Factory.StartNew(() => Process(rdr)))
                    {
                        await t;
                    }
                }

    gives different behaviour. i think i'll post this up in the Async forums to see if anyone can explain the difference in behaviour.

    thanks again,

    Paul

    • Marked as answer by Milsnips Wednesday, December 23, 2015 11:35 PM
    Wednesday, December 9, 2015 3:43 AM

All replies

  • Could you post the related code?

    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Friday, December 4, 2015 4:08 PM
  • i've added a dropbox link to my sample winforms app  with 2 buttons, one that iterates a reader on the ui thread, and one within an async task. The ui thread button executes successfully, the async thread  throws the exception. Sample MDB database is included also.

    https://www.dropbox.com/s/3wnylm4tgo0q03l/AccessDbReader.zip?dl=1


    The reader is created from the ui thread and passed into the "Process" method

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.OleDb;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace UITest
    {
        public partial class Form1 : Form
        {
            readonly string _connString = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=ManyToMany_Artists.mdb";
            private OleDbCommand _cmd;
            private readonly OleDbConnection _conn;
    
            public Form1()
            {
                InitializeComponent();
                _conn = new OleDbConnection(_connString);
                _conn.Open();
            }
            
            private OleDbDataReader GetReader()
            {
    
                _cmd = new OleDbCommand("select * from tblAlbum", _conn);
                return _cmd.ExecuteReader();
            }
    
            public void Process(OleDbDataReader reader)
            {
                try
                {
                    if (reader != null && reader.HasRows)
                    {
                        while (reader.Read())
                        {
                            WriteOutput(reader[1].ToString());
                        }
                        WriteOutput("-------------------");
                        reader.Close();
                    }
                }
                catch (Exception exception)
                {
                    WriteOutput(exception.Message);
                }  
            }
    
    
            public void WriteOutput(string msg)
            {
                if (InvokeRequired)
                {
                    Invoke((MethodInvoker)(() => output.AppendText(msg + Environment.NewLine)));
                }
                else
                {
                    output.AppendText(msg + Environment.NewLine);
                }
            }
    
    
    
    
            private async void withAsync_Click(object sender, EventArgs e)
            {
                output.Clear();
                output.AppendText(DateTime.Now + Environment.NewLine);
    
                using (var rdr = GetReader())
                {
                    using (var t = Task.Factory.StartNew(() => Process(rdr)))
                    {
                        await t;
                    }
                }
            }
    
            private void noAsync_Click(object sender, EventArgs e)
            {
                output.Clear();
                output.AppendText(DateTime.Now + Environment.NewLine);
                using (var rdr = GetReader())
                {
                    Process(rdr);
                }
            }
        }
    }
    

    Monday, December 7, 2015 10:29 PM
  • Thanks for putting your sample up on Dropbox to download. It made it so easy for me to "play" with your code.

    I don't know how correct this is (I don't have much experience with async/await), but I at least got this to work with the following changes:

    I created a new async ProcessAsync() instead of using your Process():

    // note that you need to add a "using System.Data.Common;" at the top
    public async Task ProcessAsync(DbDataReader reader)
    {
        try
        {
            if (reader != null && reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    WriteOutput(reader[1].ToString());
                }
                WriteOutput("-------------------");
                reader.Close();
            }
        }
        catch (Exception exception)
        {
            WriteOutput(exception.Message);
        }
    }

    And I changed your withAsync_Click:

    private async void withAsync_Click(object sender, EventArgs e)
    {
        output.Clear();
        output.AppendText(DateTime.Now + Environment.NewLine);
    
        // Bonnie
        _cmd = new OleDbCommand("select * from tblAlbum", _conn);
        using (DbDataReader rdr = await _cmd.ExecuteReaderAsync())
        {
            await ProcessAsync(rdr);
        }
    
        // your original code
        //using (var rdr = GetReader()) 
        //{
        // using (var t = Task.Factory.StartNew(() => Process(rdr)))
        // {
        //  await t;
        // }
        //}
    }
    
    And that's all I changed ... it works (and hopefully is correctly asynchronous).


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Tuesday, December 8, 2015 5:42 AM
  • Thanks for your reply, i can see it works ok in your example which unfortunately causes me to refactor a heap of code to make this work in my app as my plugin interface would me require to change all my plugin implementations and i dont understand why using:

    using (DbDataReader rdr = await _cmd.ExecuteReaderAsync())
        {
            await ProcessAsync(rdr);
        }

    as compared to:

    using (var rdr = GetReader())
                {
                    using (var t = Task.Factory.StartNew(() => Process(rdr)))
                    {
                        await t;
                    }
                }

    gives different behaviour. i think i'll post this up in the Async forums to see if anyone can explain the difference in behaviour.

    thanks again,

    Paul

    • Marked as answer by Milsnips Wednesday, December 23, 2015 11:35 PM
    Wednesday, December 9, 2015 3:43 AM
  • Sorry I couldn't help more. If you get an answer elsewhere, could you come back here and post it? I would really like to know the difference as well. Thanks.

    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Wednesday, December 9, 2015 5:52 AM
  • Thanks Bonnie, i can see your code sample works as a standalone solutions but for my actual implementation which are interface driven plugins  i'd need to refactor some of my generic functionality requires more testing than previously thought.
    Wednesday, December 23, 2015 11:38 PM
  • Hi Paul ... could you not find any help on other forums? I've not had time to do any other searching, but thought maybe you had found something. Guess not, eh?  :0(


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Thursday, December 24, 2015 5:56 AM