EntityFramework:如何从数据库加载对象列表?

时间:2018-02-12 11:21:22

标签: c# entity-framework

我有两个实体:

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

    public int RoomId { get; set; }
    [ForeignKey("RoomId")]
    public Room Room { get; set; }

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string DocumentNumber { get; set; }
    public string ContactPhone { get; set; }
}

public class Room
{
    [Key]
    public int RoomId { get; set; }
    public int Number { get; set; }

    public int Size { get; set; }
    public bool HasBalcony { get; set; }

    public int Beds_1 { get; set; }
    public int Beds_2 { get; set; }
    public double DayPrice { get; set; }

    public List<Booking> Bookings { get; set; }
    ...

    public int BookingsCount()
    {
        return Bookings.Count;
    }

    public bool IsFree(DateTime dateTime)
    {
       MessageBox.Show(BookingsCount().ToString());
       return true;
    }
}

和DbContext:

public class HotelContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Room> Rooms { get; set; }
    public DbSet<Booking> Bookings { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Booking>()
        .HasRequired(b => b.Room)
        .WithMany(r => r.Bookings)
        .HasForeignKey(b => b.RoomId);
    }
}

当调用MessageBox.Show时,我得到异常:类型&#39; System.NullReferenceException&#39;的未处理异常。发生在Hotel.exe

当我尝试访问Room :: Bookings时,列表始终为null。 “预订”表中有一行,“房间”表中有多行。 如何将所有预订加载到Room对象中?

4 个答案:

答案 0 :(得分:2)

取决于你在学习曲线中的位置,但有些事情很突出

<强>首先

您要么通过 FluentApi Annotations 创建关系,而不是两者

IE中。你在房间实体上有这个

[ForeignKey("RoomId")]

这很流利

 modelBuilder.Entity<Booking>()
    .HasRequired(b => b.Room)
    .WithMany(r => r.Bookings)
    .HasForeignKey(b => b.RoomId);

您需要选择其中一个,否则您可能会在Booking RoomIdRoom_Id

中结束多个ID

其次

如果您希望延迟加载 bookings,则需要Bookings收集Virtual

public virtual List<Booking> Bookings { get; set; }

<强>最后

访问您的数据(假设您的连接字符串是正确的)

using(var db = new HoteContext())
{
    var rooms = db.Rooms.Include(x => x.Bookings).ToList();
}

注意 :虽然EF Lazy加载关系,但您可能需要确保已包含Room->Booking关系

答案 1 :(得分:1)

请考虑以下代码。

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            using (MyDbContext dbContext = new MyDbContext())
            {
                dbContext.Departments.Add(new Department()
                {
                    Name = "Some Department1",
                    Employees=new List<Employee>()
                    {
                        new Employee() { Name = "John Doe" }
                    }
                });

                dbContext.SaveChanges();

                var department = dbContext.Departments.FirstOrDefault(d => d.Name == "Some Department1");

                if (department.Employees != null)
                {
                    foreach (var item in department.Employees)
                    {
                        Console.WriteLine(item.Name);
                    }
                }
            }
        }
    }

    public class Department
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Employee> Employees { get; set; }
    }

    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public DbSet<Department> Departments { get; set; }
        public DbSet<Employee> Employees { get; set; }
    }
}

如果你有上面的代码,那么控件将不会进入条件,因为department.Employees为null。现在,按如下方式更改Department实体。

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<Employee> Employees { get; set; }
}

现在你应该能够看到控制进入if条件并输出员工姓名。

这称为延迟加载

如果您想急切加载,则不必将虚拟添加到该媒体资源中。您可以按如下方式包含属性。

 var department = dbContext.Departments.Include(d => d.Employees).FirstOrDefault(d => d.Name == "Some Department1");

现在您可以看到员工姓名正在输出。

答案 2 :(得分:0)

您的设计绝对会遇到性能问题。

EF的诱惑是将对象模型完全映射到数据库,让EF在幕后为你做所有的魔术。但是你需要考虑的是在任何时候只从db获得你需要的东西。否则你会得到各种笛卡尔产品问题。我强烈建议您自己获取一份Hibernating Rhino的EF Profiler或类似内容,这样您就可以在运行时静态分析代码以解决EF性能问题(并查看它生成的SQL)。对于这个你想要的是一个专门建立的DB调用来获取计数。否则会发生什么事情,你将拉出整个预订表,然后让C#给你计数。只有你想对整个列表做些什么才有意义。有两种选择:

1)针对预订表创建一个VIEW并将其映射到EF。该视图看起来像SELECT ROOMS.ROOMID, COUNT(*) - 您将此视图映射到您的模型并 voila 现在您有一个按房间(id)计算的列表,您可以单独使用它们或将其总结获得所有房间的总数。如果您在10个房间中有1,000个预订,那么您只能从数据库中返回10行。而对于您的设计,您将使用所有字段撤回所有1,000个预订,然后使用C#进行过滤。坏juju。

2)架构和概念上更简单的方法是直接进行查询(显然这只会从db中返回一个int):

public int BookingsCount()
{
    int count = 0;

    try
    {
        using (var context = new HotelContext()) 
        { 
            var sql ="SELECT COUNT(*) FROM Bookings WHERE ROOMID=" + this.RoomId;    
            count = context.Database.SqlQuery<int>(sql).First(); 
        }
    } catch (Exception ex)
    {
        // Log your error, count will be 0 by default
    }

    return count;
}

答案 3 :(得分:-1)

一个简单的解决方案是将预订属性设为虚拟。

public class Room
{
    [Key]
    public int RoomId { get; set; }
    public int Number { get; set; }

    public int Size { get; set; }
    public bool HasBalcony { get; set; }

    public int Beds_1 { get; set; }
    public int Beds_2 { get; set; }
    public double DayPrice { get; set; }

    public virtual List<Booking> Bookings { get; set; }
}

有关实体框架加载相关实体的更多信息, https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx