Почему мой EF возвращает дубликаты строк из моего SQL View, который работает? - PullRequest
0 голосов
/ 29 августа 2018

Я поднял вопрос, но ничего, что я нашел, не работает для меня. Я создал представление в SQL, которое работает, когда вы запускаете его в Management Studio. При обращении к представлению из моего приложения MVC EF возвращает идентичные строки вместо строк с разными данными.

Таблица: Автомобили

  • [Id]
  • [Регистрация]
  • [Make]
  • [модель]

Таблица: Бронирование

  • [BookingStartDate]
  • [BookingEndDate]
  • [CarId]

Просмотр: CarBookings

SELECT  [C].[Id],
        [C].[Registration],
        [C].[Make],
        [C].[Model],
        [B].[BookingStartDate],
        [B].[BookingEndDate]

FROM [Cars] AS C INNER JOIN [Bookings] AS B ON C.Id = B.CarId

Если я запускаю запрос в SSMS, я получаю все ожидаемые результаты, например:

  • Автомобиль 1, забронировано 12/03/2018
  • Автомобиль 1, забронировано 19/09/2018

Когда я получаю доступ к тому же представлению из моего приложения MVC, я получаю:

  • Автомобиль 1, забронировано 12/03/2018
  • Автомобиль 1, забронировано 12/03/2018

Размещение точки останова на контроллере показывает, что результаты одинаковы, поэтому причиной является не уровень представления. Фильтры не применяются и нет условий вообще.

Я использую KendoUI и возвращаю свои результаты в Grid.

Вот код моего контроллера для получения данных:

HomeController.cs

public ActionResult GetBookings([DataSourceRequest] DataSourceRequest request)
{
    var bookings = unitOfWork.BookingsRepository.Get();
    var result = bookings.ToDataSourceResult(request);
    return Json(result, JsonRequestBehavior.AllowGet);
}

Мое приложение использует общий репозиторий. Я не уверен, если это вызывает проблему, но стоит упомянуть. Вот метод GET из моего хранилища.

DAL / GenericRepository.cs

public virtual IEnumerable<TEntity> Get(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = "")
{
    IQueryable<TEntity> query = dbSet;

    if (filter != null)
    {
        query = query.Where(filter);
    }

    foreach (var includeProperty in includeProperties.Split
        (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }

    if (orderBy != null)
    {
        return orderBy(query).ToList();
    }
    else
    {
        return query.ToList();
    }
}

DAL / Context.cs

public DbSet<Bookings> Bookings { get; set; }

DAL / UnitOfWork.cs

 private GenericRepository<Bookings> bookingsRepository;
 public GenericRepository<Bookings> bookingsRepository
 {
     get
     {    
         if (this.bookingsRepository == null)
         {
             this.bookingsRepository = new GenericRepository<Bookings>(context);
         }
         return bookingsRepository;
     }
 }

Класс сущности

Это класс, который представляет представление и обращается к нему с помощью аннотации [Table].

namespace MyProject.Models
{
    [Table("CarBookings")]
    public class Bookings
    {
        //Car
        [Key]
        public int Id { get; set; }
        public string Registration { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }

        //Booking
        public DateTime BookingStartDate { get; set; }
        public DateTime  BookingEndDateYearOfBuild { get; set; }
    }
}

Когда я искал ответы на это, я читал, что представление не имеет ID, поэтому EF пытается логически упорядочить записи по уникальным значениям, и это может иногда вызывать проблемы (источник: https://www.itworld.com/article/2833108/development/linq-in--net-returning-duplicate-rows-from-a-working-sql-view--solved-.html).

Я изменил свой код select в соответствии с приведенной выше статьей, но он не работал для меня; Я все еще видел дубликаты:

SELECT ROW_NUMBER() OVER (ORDER BY Car.Id) AS NID, 
    Car.Id, 
    Booking.BookingStartDate
    ... etc...

FROM Cars AS Car INNER JOIN
     Booking AS Booking ON Car.Id = Booking.Car_Id

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Я еще немного покопался и, кроме вышеупомянутого [Ключа] для представлений, другие потоки, которые я нашел, указали на .AsNoTracking() в качестве потенциального решения. Я изучил это немного подробнее и попытался реализовать это в своем решении.

Вот один из тех комментариев, касающихся моей проблемы:

Функция AsNoTracking () позволяет обойти требование "уникальный ключ на запись" в EF (не упоминается явно в других ответах).

Это чрезвычайно полезно при чтении представления, которое не поддерживает уникальный ключ, потому что, возможно, некоторые поля могут быть обнуляемыми или природа представления не может быть логически проиндексирована.

В этих случаях «ключ» может быть установлен для любого необнуляемого столбца, но тогда AsNoTracking () должен использоваться с каждым запросом, иначе записи (дублирующиеся по ключу) будут пропущены

Источник: Какая разница .AsNoTracking () имеет значение?

В моем GenericRepository.cs я установил это значение для метода Get, и результаты на моей сетке теперь точны без дублирования.

Вот код, который я изменил:

GenericRepository.cs

public virtual IEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet.AsNoTracking();

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

Это изменение решило мою проблему. Надеюсь, что в дальнейшем не будет никаких нежелательных эффектов от этого :) Спасибо всем, кто нашел время, чтобы ответить.

0 голосов
/ 30 августа 2018

Как вы уже упоминали, представления могут быть испорчены, если нет первичного ключа.

Как правило, вам нужно добавить атрибут [Key] хотя бы в одно из свойств вашего представления - первоначальная рекомендация в EF 1 с конструктором edmx состояла в том, чтобы просто установить ВСЕ свойства в представлении в качестве первичных ключей, но это может быть излишним. Но если добавление его к одному свойству не работает, попробуйте добавить его ко всем или их подмножеству, чтобы у каждого объекта был уникальный набор ключей, например

public partial class CarsBookings
{
    [Key]
    public int Id { get; set; }

    [Key]
    public string Registration { get; set; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...