内存存储列表中的 EF Core

EF Core in memory store lists

我对内存数据库概念中的整个 EF Core 还很陌生,所以我可能在这里遗漏了一些重要的东西,但我迟早需要弄清楚这一点。

我想要实现的是将一个包含其他对象列表的对象添加到数据库中。 虽然原始数据类型存储正确,但列表似乎总是空的。

public class Race
{
    public int RaceId { get; set; }
    public int Year { get; set; }
    public List<Vehicle> AllRunningVehicles { get; set; }
    public List<Vehicle> AllRepairingVehicles { get; set; }
    public List<Vehicle> AllDeadVehicles { get; set; }

    public Race()
    {
        //AllRunningVehicles = new List<Vehicle>();
        //AllRepairingVehicles = new List<Vehicle>();
        //AllDeadVehicles = new List<Vehicle>();
    }
}

这是控制器

public class RaceController : Controller
{
    private readonly RaceContext _raceContext;

    public RaceController(RaceContext Rcontext)
    {
        _raceContext = Rcontext;
    }

    [HttpPost]
    [Route("[controller]/[action]")]
    public async Task<ActionResult<Race>> PostRaceItem(int year)
    {
        Race raceItem = new Race();
        raceItem.Year = year;
        raceItem.AllRunningVehicles = new List<Vehicle>();
        raceItem.AllRepairingVehicles = new List<Vehicle>();
        raceItem.AllDeadVehicles = new List<Vehicle>();

        await _raceContext.RaceItems.AddAsync(raceItem);
        await _raceContext.SaveChangesAsync();

        return raceItem;
    }

    [HttpGet]
    [Route("[controller]/[action]")]
    public async Task<ActionResult<Race>> GetRaceItem(int raceID)
    {
        var raceItem = await _raceContext.RaceItems.FindAsync(raceID);

        if (raceItem == null)
        {
            return NotFound();
        }

        return raceItem;
    }
}

我试过在 Ctor 中创建列表,但那样它们总是空的,存储在其中的数据总是被清除,我不想这样做。我将包括 Context 和 Startup 类,以防它们需要一些我不知道的额外配置。

public class RaceContext : DbContext
{
    public RaceContext(DbContextOptions<RaceContext> options) : base(options) { }

    public DbSet<Race> RaceItems { get; set; }
}

和初创公司

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<RaceContext>(opt => opt.UseInMemoryDatabase("RaceList"));

        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "TEST", Version = "v1" });
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TEST v1"));
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

考虑一下在使用 EF 时您希望如何将数据存储在数据库中会有所帮助。在您的情况下,如果有 3 个相同的 class 集合,您提出的结构几乎是不可能的。原始类型映射到 table 中的字段,对象引用和集合映射到相关的 tables.

您有两个相关的实体:Race 和 Vehicle。假设有几辆车参加一场比赛,任何一辆车可以参加几场比赛,这就形成了多对多的关系。车辆可以是“运行”、“正在修理”或“报废”,这实际上是给定比赛中车辆的状态。

从数据库的角度来看,这意味着一个 Race table,一个 Vehicle table,然后是一个叫做 RaceVehicles 的加入 table。车辆的状态可以是 RaceVehicle table/entity:

的一部分
public class Race
{
    [Key]
    public int RaceId { get; set; }
    public int Year { get; set; }
    // ... Race details.

    public virtual ICollection<RaceVehicle> RaceVehicles { get; set; } = new List<RaceVehicle>();
}

public class Vehicle
{
    [Key]
    public int VehicleId { get; set; }
    public string Name { get; set; }
    // ... Vehicle details...
}   

public class RaceVehicle
{
    [Key, Column(Order=0), ForeignKey("Race")]
    public int RaceId { get; set; }
    [Key, Column(Order=1), ForeignKey("Vehicle")]
    public int VehicleId { get; set; }
    
    public RaceStates State { get; set; } = RaceStates.Running;

    public virtual Race Race { get; set; }
    public virtual Vehicle Vehicle { get; set; }
}

public enum RaceStates
{
    None = 0,
    Running,
    Repairing,
    Dead
}

这是第一步,但是对于一场比赛,它只会为您提供所有州的 所有 场比赛车辆。 (运行,等等)就实体而言,它们应该反映应用程序的数据状态。当我们去展示一场比赛的细节时,我们将希望根据比赛状态分解车辆。我们可以将实体传递给视图,但最好为视图提供一个格式化为只包含视图所需数据的模型。这有助于防止出现意外情况,并允许我们将数据格式化为适合视图的格式table:

[Serializable]
public class RaceViewModel
{
    public int RaceId { get; set; }
    public int RaceYear { get; set; }

    public ICollection<VehicleViewModel> RunningVehicles { get; set; } = new List<VehicleViewModel>();
    public ICollection<VehicleViewModel> RepairingVehicles { get; set; } = new List<VehicleViewModel>();
    public ICollection<VehicleViewModel> DeadVehicles { get; set; } = new List<VehicleViewModel>();

}

[Serializable]
public class VehicleViewModel
{
    public int VehicleId { get; set; }
    public string VehicleName { get; set; }
    // Vehicle details for the view.
}

当我们获取数据时,我们将我们的实体(数据状态)投射到视图模型(视图状态)中

using (var context = new AppDbContext())
{
    var viewModels = context.Races
        .Where(r => r.Year >= 2020 /*insert conditions*/)
        .Select(r => new RaceViewModel
        {
            RaceId = r.RaceId,
            RaceYear = r.Year,
            RunningVehicles = r.RaceVehicles
                .Where(rv => rv.State == RaceStates.Running)
                .Select(rv => new VehicleViewModel
                {
                    VehicleId = rv.Vehicle.VehicleId,
                    VehicleName = rv.Vehicle.Name,
                }).ToList(),
            RepairingVehicles = r.RaceVehicles
                .Where(rv => rv.State == RaceStates.Repairing)
                .Select(rv => new VehicleViewModel
                {
                    VehicleId = rv.Vehicle.VehicleId,
                    VehicleName = rv.Vehicle.Name,
                }).ToList(),
            DeadVehicles = r.RaceVehicles
                .Where(rv => rv.State == RaceStates.Dead)
                .Select(rv => new VehicleViewModel
                {
                    VehicleId = rv.Vehicle.VehicleId,
                    VehicleName = rv.Vehicle.Name,
                }).ToList()
         }).ToList();
}

这为您提供了一个模型 suitable,用于根据状态拆分车辆列表的视图。您可以在每个州的 return Vehicles 实体本身中设置类似的属性,但是您需要配置这些属性以便 EF 忽略它们,并且它们仅在加载 RaceVehicles 集合时起作用。 (要么急切加载,要么触发延迟加载(如果可用)

使用 Select 而不是 returning 实体投影的好处是 Select 或 Automapper 的 ProjectTo 将自动调整结果 SQL 以拉动从相关 tables/entities 返回任何需要的字段。如果您 returning 实体本身,您需要记住预先加载相关实体的 all 否则您将因延迟加载调用而遭受严格的性能损失或留下#null 引用,如果延迟加载被禁用。