代码之家  ›  专栏  ›  技术社区  ›  Yanayaya

为什么我的EF似乎会从正在工作的SQL视图返回重复的行?

  •  0
  • Yanayaya  · 技术社区  · 7 年前

    我已经查过这个问题了,但没有发现对我有用的东西。我在SQL中创建了一个视图,当您在Management Studio中运行它时,该视图就会工作。从我的MVC应用程序访问视图时,EF返回的是相同的行,而不是数据不同的行。

    表:汽车

    • [Id]
    • [注册]
    • [制作]
    • [模型]

    表:预订

    • [Id]
    • [预订开始日期]
    • [BookingEndate]
    • [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中运行查询,我会得到所有预期结果,例如:

    • 2018年3月12日预订的1号车
    • 2018年9月19日预订的1号车

    当我从MVC应用程序访问相同的视图时,我得到:

    • 2018年3月12日预订的1号车
    • 2018年3月12日预订的1号车

    在控制器上放置一个断点表明结果是相同的,因此不是表示层导致了它。没有应用过滤器,也没有任何条件。

    我在用 KendoUI 并将我的结果返回给 Grid .

    以下是我获取数据的控制器代码:

    家庭控制器。反恐精英

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

    我的应用程序使用通用存储库。我不确定这是否是导致问题的原因,但值得一提。这是答案 GET 方法从我的存储库中删除。

    DAL/通用报告。反恐精英

    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。反恐精英

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

    DAL/工作单元。反恐精英

     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 回复  |  直到 7 年前
        1
  •  9
  •   Community Mohan Dere    5 年前

    我做了更多的挖掘,除了上面提到的[Key]视图外,我还发现了其他指向的线索 .AsNoTracking() 作为一个潜在的解决方案。我对此进行了更多的研究,并试图在我的解决方案中实现这一点。

    以下是与我的问题有关的评论之一:

    AsNoTracking()允许绕过EF中的“每个记录的唯一密钥”要求(其他答案未明确提及)。

    这在读取不支持唯一键的视图时非常有用,因为某些字段可能为空,或者视图的性质在逻辑上不可索引。

    在这些情况下,可以将“key”设置为任何不可为null的列,但必须将AsNoTracking()用于每个查询,否则将跳过记录(按key复制)

    资料来源: What difference does .AsNoTracking() make?

    在我的 GenericRepository.cs 我把这个值设置在 Get 方法和网格上的结果现在是准确的,没有任何重复。

    以下是我更改的代码:

    一般记述。反恐精英

    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();
                }
            }
    

    这个改变解决了我的问题。希望以后不会有不必要的影响:)感谢所有花时间回复的人。

        2
  •  4
  •   Rob    7 年前

    如前所述,如果没有主键,视图可能会变得混乱。

    通常,您需要为视图上的至少一个属性添加[Key]属性——在EF 1中,edmx设计器最初的建议是将视图上的所有属性都设置为主键,但这可能有些过分。但如果将其添加到一个属性中不起作用,请尝试将其添加到所有属性或属性的子集中,以便每个实体都有一个唯一的键组合,例如。

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