Linq to Entity Framework Core 2.0表上的死锁

时间:2018-07-07 00:27:03

标签: linq asynchronous async-await entity-framework-core

尝试并行运行一堆linq查询时,我遇到了僵局。

我正在使用此方法运行QTableView

import sys
import os
sys.path.append(os.environ.get('PS_SITEPACKAGES'))
from Qt import QtGui, QtWidgets, QtCore

from functools import partial


class FilterProxy(QtCore.QSortFilterProxyModel):
    def __init__(self, *args, **kwargs):
        super(FilterProxy, self).__init__(*args, **kwargs)
        self.filters_colums = set()
        self.mText = ""

    def setText(self, text):
        self.mText = text
        self.invalidateFilter()

    def appendColumn(self, name):
        self.filters_colums.add(name.lower())
        self.invalidateFilter()

    def removeColumn(self, name):
        self.filters_colums.discard(name.lower())
        self.invalidateFilter()

    def filterAcceptsRow(self, source_row, source_parent):
        if self.mText:
            for i in range(self.sourceModel().columnCount()):
                header = self.sourceModel().headerData(i, QtCore.Qt.Horizontal) 
                text = self.sourceModel().index(source_row, i).data()
                if header in self.filters_colums and self.mText in text.lower():
                    return True
            return False
        return True


class QDictTableView(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super(QDictTableView, self).__init__(*args, **kwargs)
        self.resize(400,300)

        # controls
        self.ui_search_input = QtWidgets.QLineEdit()
        self.ui_search_input.setPlaceholderText('Search...')

        self.ui_item_table = QtWidgets.QTableView()
        self.ui_item_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.ui_item_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.ui_item_table.verticalHeader().hide()

        lay_filters = QtWidgets.QHBoxLayout()
        lay_main = QtWidgets.QVBoxLayout(self)
        lay_main.setAlignment(QtCore.Qt.AlignTop)
        lay_main.addWidget(self.ui_search_input)
        lay_main.addLayout(lay_filters)
        lay_main.addWidget(self.ui_item_table)

        model = self.populate_table()
        self.proxy = FilterProxy()
        self.proxy.setSourceModel(model)
        self.ui_item_table.setModel(self.proxy)

        for text in ("Name", "Age", "Career"):
            checkbox = QtWidgets.QCheckBox(text)            
            checkbox.stateChanged.connect(partial(self.update_columns, text))
            checkbox.setChecked(True)
            lay_filters.addWidget(checkbox)

        self.ui_search_input.textChanged.connect(self.proxy.setText)

    def update_columns(self, text, state):
        cols = self.ui_item_table.model().columnCount()
        i = [self.ui_item_table.model().headerData(i, QtCore.Qt.Horizontal) for i in range(cols)].index(text.lower())
        self.ui_item_table.setColumnHidden(i, state == QtCore.Qt.Unchecked)
        if state == QtCore.Qt.Unchecked:
            self.proxy.removeColumn(text)
        else:
            self.proxy.appendColumn(text)

    def populate_table(self):
        peoples = [
            {'name': 'Kevin', 'age': 5, 'career': 'athlete'},
            {'name': 'Maggie', 'age': 13, 'career': 'banker'},
            {'name': 'Leslie', 'age': 32, 'career': 'banker'},
            {'name': 'Emily', 'age': 45, 'career': 'athlete'},
            {'name': 'David', 'age': 27, 'career': 'banker'},
            {'name': 'Marie', 'age': 63, 'career': 'secretary'}
        ]

        model = QtGui.QStandardItemModel()

        headers = ["name", "age", "career"]
        model.setHorizontalHeaderLabels(headers)

        for row, people in enumerate(peoples):
            items = []
            for key, value in people.items():
                col = headers.index(key)
                item = QtGui.QStandardItem(str(value))
                items.append(item)
            model.insertRow(row, items)
        return model

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = QDictTableView()
    ex.show()
    sys.exit(app.exec_())

我得到一个Task.WhenAll()

public async Task<MetabuildScan> GetLatestMetabuildScanAsync(string buildId) { var metabuildScanStatuses = new[] { "Completed", "Referenced" }; // Get the latest metabuild scan for this image build var latestScan = await (from scan in _qsaContext.MetabuildScans where scan.SoftwareImageBuildId == buildId && metabuildScanStatuses.Contains(scan.SIScanStatus) orderby scan.SIScanStartedOn descending select scan).FirstOrDefaultAsync(); // If there is a related scan, then use that one, else, use the one we just got var latestCompletedScanId = latestScan?.RelatedScanId ?? latestScan?.Id; return await _qsaContext.MetabuildScans .FirstOrDefaultAsync(scan => scan.Id == latestCompletedScanId); } 是使用Entity-Framework Core创建的。

起初,我以为System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.可以解决我的问题(起初我有一个非异步的_qsaContext),但是没有。

我想知道解决这个僵局的最佳解决方案是什么。我从中选择的表是一个大表,因此我无法将整个表拉入内存。

2 个答案:

答案 0 :(得分:0)

与往常一样,将查询包裹在try / catch中,然后重复您的交易。

答案 1 :(得分:0)

实体框架DbContext不是线程安全的。您不能像尝试那样对它运行并行查询。

如果您需要一个共享的查询上下文,则需要逐个顺序地等待每个查询。

如果它们不需要共享的上下文,但是需要并行运行,则每个查询都需要有一个单独的上下文。

如果使用DI框架,也许您可​​以研究使DbContext成为瞬态对象(而不是我假设的是Scoped对象),并将其注入到将调用您的查询方法的查询类中。