Entity Framework: замена всей коллекции DbSet

У меня есть универсальный класс, который выполняет добавление / обновление объектов типа T. AddOrUpdate() метод принимает в DbSet коллекция для действия, а также список элементов для добавления или обновления в DbSet, ItemExists() используется для проверки наличия элемента в коллекции. Если это так, мы обновляем. Если нет, мы добавим. Метод по существу сравнивает первичный ключ элемента, переданного с каждым отдельным элементом в таблице, и возвращает true (а также сам объект базы данных), если есть совпадение.

Код отлично работает для таблиц с небольшим количеством записей. Однако для больших таблиц ItemExists() Метод очень неэффективен. В этом методе используется цикл foreach, который находится внутри другого цикла foreach в методе вызывающей стороны, давая O(n^2).

Более простым способом было бы просто использовать contextDataSet.Contains(item), но это бросает исключение, которое говорит Unable to create a constant value of type что имеет смысл, поскольку EF не может перевести класс в запрос SQL. Так что это не пойдет.

Теперь мой актуальный вопрос: есть ли способ заменить весь DbSet<T> с IEnumerable<T> что передается? IEnumerable, который передается, привязан к сетке данных в представлении и, по сути, включает в себя все элементы, поэтому, логически говоря, замена всей коллекции должна быть безопасной. Любая помощь с благодарностью.

Код

public void AddOrUpdate<I, P>(Expression<Func<I, P>> dbSetExpression, IEnumerable<T> itemsToUpdate)
            where I : DbContext, new()
            where P : DbSet<T>
{
    DataFactory.PerformOperation<I>(c =>
    {
        if (m_primaryKey == null && !TryFindPrimaryKey(c))
        {
            throw new ArgumentException("Primary key cannot be null.");
        }

        // Get the table name from expression passed in.
        string dbsetName = ((MemberExpression)dbSetExpression.Body).Member.Name;

        var propertyInfo = c.GetType().GetProperties().Single(p => p.Name == dbsetName);

        // Get the values in the table.
        DbSet<T> contextDataSet = propertyInfo.GetValue(c) as DbSet<T>;


        foreach (var item in itemsToUpdate)
        {

            // If the primary key already exists, we're updating. Otherwise we're adding a new entity.
            T existingItem;

            if (ItemExists(contextDataSet, item, out existingItem) && existingItem != null)
            {
                c.Entry(existingItem).CurrentValues.SetValues(item);
            }
            else
            {
                contextDataSet.Add(item);
            }
        }

        c.SaveChanges();
    });
}

private bool ItemExists(DbSet<T> itemInDbSet, T itemInList, out T existingItem)
{
    foreach (var dbItem in itemInDbSet)
    {
        // Get the primary key value in the database.
        var dbValue = dbItem.GetType().GetProperties().Single(
            p => p.Name == m_primaryKey).GetValue(dbItem);

        // Get the primary key value from the item passed in.
        var itemValue =
            itemInList.GetType().GetProperties().Single(
            p => p.Name == m_primaryKey).GetValue(itemInList);

        // Compare the two values.
        if (dbValue.ToString() == itemValue.ToString())
        {
            existingItem = dbItem;
            return true;
        }
    }

    existingItem = null;
    return false;
}

0 ответов

Другие вопросы по тегам