内存存储列表中的 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 引用,如果延迟加载被禁用。
我对内存数据库概念中的整个 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 引用,如果延迟加载被禁用。