Почему IQueryable.Select использует одну и ту же ссылку для каждой итерации - PullRequest
0 голосов
/ 12 октября 2018

У меня есть IQueryable, на котором я хочу выполнить Select.В этом случае я создаю новый экземпляр объекта и запускаю функцию, которая копирует значения объекта из IQueryable (b) во вновь созданный объект (новый DTO) и затем возвращает этот экземпляр.

IQueryable.Select:

businessLayer.GetAll().Select( b => new DTO().InitInhertedProperties(b)).ToList();

Функция в DTO:

public DTO InitInhertedProperties(Base baseInstance)
{
    return Utilities.InitInhertedProperties(this, baseInstance);
}

Функция для копирования:

public static T InitInhertedProperties<T,K>(T instance, K baseClassInstance)  where T : K
{
    foreach (PropertyInfo propertyInfo in baseClassInstance.GetType().GetProperties())
    {
        object value = propertyInfo.GetValue(baseClassInstance, null);
        if (null != value) propertyInfo.SetValue(instance, value, null);
    }

    return instance;
}

Первый раз InitInhertedProperties метод вызывается instance - пустой объект, baseClassInstance имеет значения, которые он должен иметь:

enter image description here

Результат первогоИтерация выглядит следующим образом: enter image description here

Как видите: все работало так, как должно быть на первой итерации.Теперь вторая итерация.

Во второй раз, когда метод InitInhertedProperties вызывается insatnce, это не новый экземпляр, а один из первой итерации.baseClassInstance - это именно то, что должно быть:

enter image description here

Результат второй итерации выглядит следующим образом: enter image description here

Полученный список выглядит следующим образом: enter image description here

Это происходит только при использовании IQueryable.Select.При использовании List.Select результат выглядит точно так же, как и ожидалось.

Это означает, что решение этой проблемы устранено.Но это просто обходной путь, а не решение.

businessLayer.GetAll().ToList().Select( b => new DTO().InitInhertedProperties(b)).ToList(); 

1 Ответ

0 голосов
/ 12 октября 2018

Когда вы работаете с IQueryable, вы привязаны к выражениям.Entity Framework проверит каждое выражение, которое вы поместите в Select, OrderBy и другие методы, и попытается преобразовать его в SQL.Таким образом, вы не можете вызывать произвольные методы внутри своей лямбды, известные только EF

. Если вы хотите что-то сделать, что не имеет прямой поддержки от движка SQL, вы можете вызвать AsEnumerable:

businessLayer.GetAll().AsEnumerable().Select( ... 

(Обратите внимание, что AsEnumerable лучше, чем ToList, потому что он сохраняет лень)

Другой вариант, который может (или не может, в зависимости от версии поставщика запросов), состоит в том, чтобы создать выражение вручную:

public static Expression<Func<TEntity, TDto>> InitInhertedProperties<TEntity, TDto>() where TDto : TEntity
{
    var entity = Expression.Parameter(typeof(BusinessObject), "b");
    var newDto = Expression.New(typeof(Dto).GetConstructors().First());
    var body = Expression.MemberInit(newDto,
        typeof(TDto).GetProperties()
            .Select(p => Expression.Bind(p, Expression.Property(entity, p.Name)))
      );

    return Expression.Lambda<Func<TEntity, TDto>>(body, entity);
}

Использование:

var myExp = InitInhertedProperties<BusinessObject, Dto>();
var result = businessLayer.GetAll().Select(myExp).ToList();
...