【发布时间】:2026-01-03 16:40:01
【问题描述】:
我将 MVVM 与 EF Core 一起使用。我有一个实体,它具有预先播种到数据库的外国属性,如下所示:
public class STOCK : EntityBase
{
public string TEXT {get;set;}
public decimal AMOUNT {get;set;}
private TAX SALESTAX {get;set;}
[ForeignKey("SALESTAX")]
public int SALESTAX_ID {get;set;}
private TAX SPECIALTAX {get;set}
[ForeignKey("SPECIALTAX")]
public int SPECIALTAX_ID {get;set;}
}
public class TAX
{
public int TAXCODE {get;set;}
public string NAME {get;set;}
public ICollection<STOCK> STOCK_TAX {get;set;}
public ICollection<STOCK> STOCK_SPECIAL {get;set}
}
public class EntityBase, INotifyPropertyChanged
{
public int ID {get;set;}
//The interface is fully implemented here. Removed for brevity.
}
在我的 ViewModel 上,我同时注入了 StockDataService 和 TaxDataService,如下所示:
public class StockDataService : IStockDataService
{
private readonly MyDbContextFactory _factory;
public StockDataService(MyDbContextFactory factory)
{
_factory = factory;
}
public async Task<STOCK> Create(STOCK entity)
{
using MyDbContext context = _factory.CreateDbContext();
STOCK createdResult = await context.STOCKs.AddAsync(entity);
return createdResult;
} //Other CRUD methods are implemented as well, but removed for brevity.
}
public class TaxDataService : ITaxDataService
{
private readonly MyDbContextFactory _factory;
public TaxDataService(MyDbContextFactory factory)
{
_factory = factory;
}
public async Task<TAX> GetAll()
{
using MyDbContext context = _factory.CreateDbContext();
return await context.TAXs.ToListAsync();
}//Ditto
}
我的视图有一个组合框如下:
<ComboBox ItemsSource="{Binding TAXES}"
SelectedValue="{Binding SALESTAX, Mode=TwoWay, UpdateSourceTrigger=Default}"
SelectedItem="{Binding SALESTAX}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0">
<Border Padding="0,0,2,0" BorderThickness="1" BorderBrush="LightGray">
<TextBlock>
<Run Text="{Binding TAXCODE}"/>
</TextBlock>
</Border>
<Border Padding="2,0,0,0" BorderThickness="1" BorderBrush="LightGray">
<TextBlock>
<Run Text="{Binding TEXT}"/>
</TextBlock>
</Border>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
我的viewModel如下:
public class StockCrudViewModel
{
private readonly IStockDataService _stockDataService;
private readonly ITaxDataService _taxDataService;
public STOCK CurrentStock {get;set;}
public ICollection<TAX> TAXES {get;set;}
public StockCrudViewModel(IStockDataService stockDataService, ITaxDataService taxDataService)
{
_stockDataService = stockDataService;
_taxDataService = taxDataService;
FillTaxesList();
}
private async void FillTaxesList()
{
TAXES = new List<TAX>(await _taxDataService.GetAll());
}
“保存”命令如下:
await _stockDataService.Create(_stockCrudViewModel.STOCK);
//Both the data service as well as the scoped view model are passed via dependency injection to the command.
编辑:我的DbContextHostBuilder 如下:
host.ConfigureServices((context, myServices) =>
{
string connString = context.Configuration.GetConnectionString("default");
Action<DbContextOptionsBuilder> configureDbContext = c => { c.UseMySql(connString); c.EnableSensitiveDataLogging(); };
myServices.AddSingleton<MyDbContextFactory>(new MyDbContextFactory(configureDbContext));
myServices.AddDbContext<MyDbContext>(configureDbContext);
});
现在,我明白为什么这样做会引发“尝试创建重复条目”异常,因为通过绑定设置 SALESTAX 属性将使用来自 MyDbContext 不同实例的 TAX,所以EF Core 无法相应地跟踪它。但是,由于我使用的是AddDbContext,并且传递了工厂,而不是上下文本身,因此每次调用数据服务方法之一时都会实例化一个新的上下文,我不确定如何让 EF Core 知道存在已经存在应该使用的 TAX 条目。
根据How to save an entity with a child entity which already exists in EF core?,他们建议使用用于创建新条目的相同上下文来获取现有条目。但是如果我的实体有五个或更多的外国财产,那不会影响业绩吗?还是我在规划 MVVM 架构时搞砸了?
【问题讨论】:
-
如果对象设置了 ID,您可以在保存更改之前对其调用
DbContext.Update。顺便说一句,在命名约定方面,您无处不在。 -
对不起,为了清楚起见,我只是将原始名称翻译成英文。
标签: c# wpf entity-framework mvvm