无法将COM对象转换为类类型

3

我正在制作一个图书馆管理软件。我将数据库管理分离到一个独立的C#库“DataAccessLibrary”中。它包含DataAccess.cs:

public static event MyDelegate Event_AddBook;

    public static void InitializeDatabase()
    {
        using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
        {
            db.Open();

            //Not necessary
        }
    }

    public static void AddBook(Book book)
    {
        Event_AddBook.Invoke(book);

        using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
        {
            db.Open();
            //Not necessary
        }            
    }

    public static ObservableCollection<Book> GetBooks()
    {
        ObservableCollection<Book> books = new ObservableCollection<Book>();

        using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
        {
            db.Open();
            //Not necessary
        }

        return books;
    }
}

当一本书被添加到数据库中时,它会触发事件,以便我的YourBooks_View类(显示书籍)被通知有一本书被添加了。
(注意:使用新窗口添加书籍。)
在其他某个文件中,我像这样向数据库添加一本书:
Book b = new Book
{
    Title = title,
    Author = author,
    Publisher = publisher,
    ISBN = isbn,
    Quantity = quantity.GetValueOrDefault(),
    CoverImageLocation = CoverImageUri
};

DataAccess.AddBook(b);

YourBooks类订阅了事件“Event_AddBook”:
public sealed partial class YourBooks_View : Page
{
    private BooksViewModel ViewModel { get; set; } = new BooksViewModel();

    public YourBooks_View()
    {
        this.InitializeComponent();
        DataAccess.Event_AddBook += new MyDelegate(this.GridView_AddBook);
    }

    private void GridView_AddBook(Book book)
    {
        ViewModel.Books.Add(book);
    }
}

代码编译时没有错误,但在运行时添加书籍时,抛出了以下异常,位于 YourBooks_View.GridView_AddBook() 行:
System.InvalidCastException
HResult=0x80004002
Message=无法将 COM 类型为 'System.Collections.Specialized.NotifyCollectionChangedEventHandler' 的 COM 对象转换为非 COM 类型 'System.Collections.Specialized.NotifyCollectionChangedEventHandler'。表示 COM 组件的类型的实例不能强制转换为不表示 COM 组件的类型;但是只要基础的 COM 组件支持 QueryInterface 调用该接口的 IID,它们就可以强制转换为接口。
Source=System.Private.CoreLib
StackTrace: at System.StubHelpers.StubHelpers.GetCOMIPFromRCW_WinRTDelegate(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget) at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection1.InsertItem(Int32 index, T item) at System.Collections.ObjectModel.Collection`1.Add(T item) at Bookshelf.YourBooks_View.GridView_AddBook(Book book) in C:\Users\Hemil\source\repos\Bookshelf\Bookshelf\YourBooks_View.xaml.cs:line 54 at DataAccessLibrary.DataAccess.AddBook(Book book) in C:\Users\Hemil\source\repos\Bookshelf\DataAccessLibrary\DataAccess.cs:line 44 at Bookshelf.NewBook_View.d__3.MoveNext() in C:\Users\Hemil\source\repos\Bookshelf\Bookshelf\NewBook_View.xaml.cs:line 137
我不知道这意味着什么。是否有方法解决这个问题或以其他方式通知视图。
对于c#,我不是很擅长,请原谅我的任何错误或愚蠢的问题。
编辑:我使用 Microsoft.Data.Sqlite 容器中提供的 Sqlite 数据库。
编辑:我遵循了此教程:https://learn.microsoft.com/en-us/windows/uwp/data-access/sqlite-databases。 我正在针对最新的 Windows 10,因此没有将 Sqlite 包含在我的应用程序中。
编辑:我将整个源代码上传到 gitlab 公共存储库中:https://gitlab.com/rahem027/bookshelf2 编辑:我认为我也应该包括 BookViewModel 源代码。好吧,这里它是:
public class BooksViewModel
{
    private ObservableCollection<Book> books { get; set; }

    public ObservableCollection<Book> Books
    {
        get { return this.books; }

        set
        {
            this.books = value;
        }
    }


    public BooksViewModel()
    {
        books = DataAccess.GetBooks();
    }
}

听起来有点奇怪,但是你可以尝试将抛出异常的代码块包装在 Task.Run 块中吗? - TheTanic
我不明白你的意思,@TheTanic。你能展示一下你的想法吗? - Hemil
将抛出错误的代码行包装在新的Task.Run块中,以便在另一个任务中执行。我遇到过这个错误多次,这种方法对我很有效!或者特别指定使用UI Dispatcher!目前我还没有电脑,所以无法帮助您编写代码。 - TheTanic
@TheTanic,这是我做的:Task.Run(() => ViewModel.Books.Add(book));。它给了我这个错误 System.Runtime.InteropServices.COMException HResult=0x8001010E Message=应用程序调用了一个为不同线程编组的接口。(HRESULT 异常: 0x8001010E (RPC_E_WRONG_THREAD)) - Hemil
我今天尝试了一下,你需要使用Dispatcher线程...抱歉我不确定哪一个是必需的。请参考Breeze Liu的答案。 - TheTanic
2个回答

3
您遇到此问题的原因是从 New View 的线程更新了 Main View 的 ViewModel。当单击“Add”按钮时,将触发 AddBook 事件,这是在 New View 的线程上进行的,但您在 YourBooks_View 类中更新了 Main View 的 ViewModel。您应该使用 CoreDispatcher.RunAsync 方法在新视图的 UI 线程上安排工作。

因此,只需更改您的 GridView_AddBook 方法代码,使用 CoreDispatcher.RunAsync 方法安排工作在 UI 线程上添加新的 Book 对象即可。

以下是修改后的 GridView_AddBook 方法的代码:

private async void GridView_AddBook(Book book)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        ViewModel.Books.Add(book);
    });
}

0

你正在尝试添加到集合中的书籍对象可能已经附加到数据库中,当你执行与数据库的操作时,请确保Dispose它的上下文(这取决于你使用的数据库提供程序)。

因此,为了解决这个问题,你可以尝试将书籍对象复制到一个新实例中,并将其放入你的ViewModel集合中,如下所示:

private void GridView_AddBook(Book book)
{
    Book b = new Book
    {
        Title = book.title,
        Author = book.author,
        Publisher = book.publisher,
        ISBN = book.isbn,
        Quantity = book.quantity.GetValueOrDefault(),
        CoverImageLocation = book.CoverImageUri
    };
    ViewModel.Books.Add(b);
}

请问您使用的是哪个数据库提供商?能否展示一些访问数据库的代码? - Muhammad Touseef
我更新了问题。你还有疑问吗? - Hemil
在你的代码中,你已经注释了//不必要的东西,我认为那段代码是你正在访问数据库的代码,所以检查它非常重要。我只想确认一下你是否在处理完dbcontext后进行了处理或将其包装在using语句中? - Muhammad Touseef
你的代码看起来很好,似乎在那种情况下出现了一些奇怪的错误。 - Muhammad Touseef
问题似乎与COM对象有关。我再次阅读了错误信息,我认为就是这个问题。 - Hemil
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接