Как я могу сказать, почему / как некоторые запросы LINQ работают друг с другом, а другие нет? - PullRequest
0 голосов
/ 14 июня 2019

Возьмите следующий класс EF:

    public class Person
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public IEnumerable<Property> Property { get; set; }
    }

    public class Property
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public bool Lock { get; set; }
        public Person Person { get; set; }
        public int PersonID
    }

Я могу в значительной степени заставить все работать как положено - включая действие удаления для Person, которое также удаляет все их свойства.Однако, поскольку мой код усложняется, я хочу сделать логику немного более продвинутой.

В приведенном выше примере у нас есть что-то еще, что установит bool lock для свойства.В этом случае я хочу отключить удаление для пользователя, когда любое свойство для этого человека имеет блокировку true.

Код контроллера удаления по умолчанию имеет:

public async Task<IActionResult> Delete(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        var person = await _context.People
            .FirstOrDefaultAsync(m => m.ID == id);
        if (person== null)
        {
            return NotFound();
        }

        return View(person);
    }

И подтверждение удаления имеет:

   public async Task<IActionResult> DeleteConfirmed(int id)
   {
       var person= await _context.people.FindAsync(id);
       _context.people.Remove(person);
       await _context.SaveChangesAsync();
       return RedirectToAction(nameof(Index));
   }

Я знаю код для выполнения того, что я хочу:

foreach (var item in person.Property)
{
    if item.locked==true
        return("error")
}

Теперь веселые звезды!- Старое виртуальное ключевое слово EF4 для свойств, к которым я привык, не работает, поэтому я не могу перебрать свойство, потому что оно в настоящее время равно нулю.в большинстве случаев мне приходится звонить .include()

При первом удалении это изменяет:

    var person = await _context.People
        .FirstOrDefaultAsync(m => m.ID == id);

на

    var person = await _context.People.Include(x=>x.property)
        .FirstOrDefaultAsync(m => m.ID == id);

, что, кажется, работает нормально.

Однако второе:

    var person = await _context.people.FindAsync(id);

, похоже, не работает.В тот момент, когда я вставляю .Include, он сообщает об ошибке CS1061, что для FindAsync нет определения.

Честно говоря, я не слишком уверен, зачем нужны два разных способа просмотра идентификатораво-первых ... Я могу только предположить, что при поиске идентификатора в первом удалении, который может не существовать, firstordefault является лучшим, а при подтверждении удаления, find является лучшим .... однако этоэто то, что делает строительные леса, и я не чувствую, что знаю достаточно, чтобы подвергнуть сомнению это.

Я, однако, хочу быть лучшим разработчиком и хотел бы понять, что не так с кодом и в будущем, как мне это сделать?знаю, что можно комбинировать, а что нет, поскольку я не чувствую, что я здесь учусь, я просто случайным образом пробую разные вещи, пока не найду одну работающую комбинацию.

1 Ответ

0 голосов
/ 14 июня 2019

Несколько вещей: Я бы посоветовал проверить, заблокирован ли пользователь, перед тем, как включить кнопку «Удалить», или сразу после нажатия кнопки «Удалить», а не после подтверждения удаления.

С этим кодом:

var person = await _context.People
   .FirstOrDefaultAsync(m => m.ID == id);
if (person== null)
    return NotFound();

return View(person);

Объекты должны представлять состояние данных, а не состояние просмотра. Возврат сущностей в представление приведет к проблемам. Если отложенная загрузка поддерживается / включена, это может вызвать проблемы с производительностью, когда отложенные нагрузки запускаются сериализацией, это также может привести к ошибкам из-за циклических ссылок. Он предоставляет больше информации о вашей структуре данных и данных в целом, которые не нужны клиенту (больше данных по сети и больше информации для хакеров). Вместо этого используйте POCO-класс ViewModel, содержащий только те данные, которые нужны вашему представлению, и используйте .Select() для его заполнения.

Далее, избегайте костылей FirstOrDefault. Это может привести к тому, что непреднамеренные ошибки останутся скрытыми. Если ожидается 1 объект, используйте Single или SingleOrDefault. Ваше приложение должно обрабатывать исключения изящно и беспристрастно. Если кто-то отправляет неверный идентификатор, не удается и прекратить сеанс По сути, не доверяйте клиенту не вмешиваться.

var person = await _context.People
   .Select(x => new PersonViewModel
   {
       PersonId = x.ID,
       Name = x.FirstName + " " + x.LastName,
       // etc.
   }).SingleAsync(x => x.ID == id);

return View(person);

Проверяя состояние данных при подтверждении, получении и идентификаторе и хотите подтвердить его перед выполнением удаления, вы можете запросить необходимую информацию, но для удаления вам не нужен весь объект, если вы доверяете идентификатору. Что-то вроде этого не нужно:

foreach (var item in person.Property)
{
    if item.locked==true
        return("error")
}

Вместо того, чтобы:

var isLocked = context.People.Where(x => x.ID == id)
    .Select(x => x.Property.Any(p => p.isLocked))
    .Single();

Выдается, если идентификатор человека не найден, и возвращает Bool True of False, если какие-либо записи в свойствах для этого человека заблокированы.

Оттуда вы можете использовать простой «трюк» для удаления объекта без предварительной загрузки:

if (!isLocked)
{
   var person = new Person { ID = id };
   context.People.Attach(person);
   context.People.Remove(person);
   context.SaveChanges();
}

В качестве альтернативы, если вы хотите загрузить Person, чтобы иметь доступ к другим свойствам, таким как создание записи аудита, или вы можете в любом случае отображать информацию как часть сообщения об ошибке, то вы можете заменить приведенные выше примеры на:

var personData = context.People.Where(x => x.ID == id)
    .Select(x => new 
    {
       Person = x,
       IsLocked = x.Property.Any(p => p.isLocked))
    }).Single();

if (!personData.isLocked)
{
   context.People.Remove(personData.Person);
   context.SaveChanges();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...