这是因为我有
public class Language
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("native_name")]
public string NtiveName { get; set; }
[Column("code_1")]
public string Code1 { get; set; }
[Column("code_2")]
public string Code2 { get; set; }
}
public class UserCreateModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string UserName { get; set; }
[EmailAddress]
[Required]
public string VerificationEmail { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
public bool IsBusinessAccount { get; set; }
[Required]
public Language Language { get; set; }
}
这两个模型,当尝试创建语言时,它已经存在我可以防止在语言表中创建语言而只引用它吗?
答案 0 :(得分:0)
创建一对多关系。将 Id 和 LanguageId 属性添加到 UserCreateModel
public class UserCreateModel
{
[Key]
public int Id {get; set;}
.....
[Required]
public int? LanguageId { get; set; }
[ForeignKey(nameof(LanguageId))]
[InverseProperty("UserCreateModels")]
public Language Language { get; set; }
}
public class Language
{
[Column("id")]
[Key]
public int Id { get; set; }
....
[InverseProperty(nameof(UserCreateModel.Language))]
public virtual ICollection<UserCreateModel> UserCreateModels { get; set; }
}
答案 1 :(得分:0)
看起来您将实体(语言)与视图模型混合在一起。 (用户创建模型)
UserCreateModel 通常根本不需要语言实体或对象引用。这可能是您从从 Languages 实体加载的下拉列表中选择的内容,在这种情况下,我通常建议在 UserCreateModel 中只包含 LanguageId。
然后,当您从 UserCreateModel 创建一个 User 实体时,您的代码将如下所示:
using (var context = new AppDbContext())
{
Language language = context.Languages.Single(x => x.LanguageId == userCreateModel.LanguageId);
User user = new User
{
FirstName = userCreateModel.FirstName,
LastName = userCreateModel.LastName,
UserName = userCreateModel.UserName,
Language = language
};
context.Users.Add(user);
context.SaveChanges();
}
您的当前视图模型中可能有一个看起来像 Language 实体的东西,但是当从您的视图中反序列化时,它是一个全新的数据引用,看起来就像一个 Language 实体。 DbContext 对此一无所知。您可以通过在 DbContext 上使用 Attach
将 Language 对象与 DbContext 关联来解决此问题,但如果该 DbContext 可能已经加载/附加了该语言引用,则应始终首先检查本地中的现有引用缓存并使用该引用。在这种情况下,我假设 DbContext 的范围在此代码之外,例如注入控制器并由 IoC 容器管理:
Language language = context.Languages.Local.SingleOrDefault(x => x.LanguageId == userCreatedModel.Language.LanguageId);
if (language == null)
{
context.Attach(userCreatedModel.Language);
language = userCreatedModel.Language;
}
User user = new User
{
FirstName = userCreateModel.FirstName,
LastName = userCreateModel.LastName,
UserName = userCreateModel.UserName,
Language = language
};
context.Users.Add(user);
context.SaveChanges();
这里的区别在于我们首先检查 .Local
DbSet 缓存以获取对该语言的引用。这不会影响数据库。如果我们找不到一种语言,我们可以附加提供的语言并关联该语言。否则我们将使用本地的。这仍然不是万无一失的,因为如果传入调用的语言不存在,我们仍然可以获得异常。 (被其他请求删除,或被篡改无效)
如果 User 实体公开 LanguageId FK 属性,那么您可以放弃加载 Language 并仅使用 LanguageId = userCreateModel.LanguageId
但是通过加载 Language 您确保创建的 User 实体(并可能返回等)具有完整有效的语言参考集。在 SaveChanges
之后,将更新任何 LanguageId FK 属性。如果传递了无效的 LanguageId,则通过加载语言,您将在该点出现异常,指出未找到该语言。如果您只设置 LanguageId,您将在 SaveChanges
上对 User 进行 FK 约束违规。