[英]One-to-one Entity Framework relationship
在我的数据模型(Entity Framework 6.1.3,ASP.NET MVC 5,使用现有数据库的Code First)中,有两个表,“ Person ”和“ User ”共享一对一的关系。 “用户”表的“ PersonID”列作为PK ,而“人”表的PK依次为。 我希望每当创建新的User记录时,都会自动创建一个Person记录,然后将Person表中PersonID的值插入到新的User记录中。
这是Person表的模型代码:
[Table("Person")]
public partial class Person
{
public int PersonID { get; set; }
public virtual User User { get; set; }
}
这是User表的模型代码:
[Table("User")]
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PersonID { get; set; }
[Required]
[StringLength(20)]
public string Name { get; set; }
public virtual Person Person { get; set; }
}
UserController.cs代码包括:
// POST: User/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "PersonID,Name")] User user)
{
if (ModelState.IsValid)
{
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.PersonID = new SelectList(db.People, "PersonID", "PersonID", user.PersonID);
return View(user);
}
Person表的SQL:
CREATE TABLE [dbo].[Person](
[PersonID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL
CONSTRAINT [PK_Person_PersonID] PRIMARY KEY CLUSTERED)
用户表的SQL:
CREATE TABLE [dbo].[User](
[PersonID] [int] NOT NULL,
[Name] [nvarchar](20) NOT NULL
CONSTRAINT [PK_User_PersonID] PRIMARY KEY CLUSTERED)
ALTER TABLE [dbo].[User] WITH CHECK ADD CONSTRAINT [FK_User_Person_PersonID] FOREIGN KEY([PersonID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[User] CHECK CONSTRAINT [FK_User_Person_PersonID]
GO
预先感谢galaf。
您的数据库结构应如下所示
将上述数据库结构转换为实体框架配置
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure PersonID as PK for User
modelBuilder.Entity<User>()
.HasKey(e => e.PersonID);
// Configure PersonID as FK for User
modelBuilder.Entity<Person>()
.HasRequired(p => p.User)
.WithRequired(u => u.Person);
}
如果您尝试单独保存不带User
Person
,则上述配置(1-1关系)将出现错误,反之亦然。
注意 :我建议建立一个(1-0..1关系),因为从逻辑上讲,
User
严格取决于人(弱实体),而Person
可以在没有用户的情况下单独存在。 1:0..1的配置与上述配置非常相似,唯一的区别是通过使.HasOptional(p=>p.User)
代替.HasRequired(p=>p.User)
现在,当您创建User实体时,您的代码应如下所示
var user = new User(){
Name = "UserName",
Person = new Person(){
FirstName="FirstName",
LastName ="LastName",
...
}
};
db.Users.Add(user);
db.SaveChanges();
每当您创建用户对象时,以上代码将强制创建人员对象。
更新:针对您的情况 :
我建议您创建一个DTO对象来管理两个对象(用户和个人)的CRUD,如下所示
public class UserDTO
{
public int Id { get; set;}
public string UserName { get;set;}
public string Password { get;set;}
public string FirstName { get; set;}
public string LastName { get; set;}
// ... any other required properties goes here ... //
}
您的UserController.cs
代码可以按以下操作处理创建和编辑操作,只要两个视图相同,就可以减少编写代码。
[HttpGet]
public ActionResult CreateOrEdit(int? id)
{
UserDTO user = null;
if( id!=null)
user = db.Users.Select(t=> new UserDTO {
UserName = t.UserName,
FirstName = t.Person.FirstName,
LastName = t.Person.LastName,
Id = t.Id
// ... //
}).FirstOrDefault();
else
user = new UserDTO();
return View(user);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(UserDTO model)
{
if(model!=null && ModelState.IsValid)
{
if(model.Id>0) // Edit
{
User user = db.Users.Include(p=>p.Person).Single(t=>t.Id == model.Id);
user.UserName = model.UserName;
user.Person.FirstName = model.FirstName;
user.Person.LastName = model.LastName;
}
else // Add
{
User user = new User
{
UserName = model.UserName,
Password = model.Password, // should be encrypted first
Person = new Person{
FirstName = model.FirstName,
LastName = model.LastName
}
};
db.Users.Add(user);
}
db.SaveChanges();
return RedirectoToAction("TargetActionGoesHere");
}
return View(model);
}
您的View CreateOrEdit.cshtml
应该看起来像,我不会仅仅为了简化而添加html代码和标签
@model UserDTO
@using(Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
@* To store the value of Id in hidden field*@
@Html.HiddenFor(m=>m.Id)
@Html.EditorFor(m=>m.FirstName)
@Html.EditorFor(m=>m.LastName)
@Html.EditorFor(m=>m.UserName)
@if(Model.Id ==0 )
{
// only in the add case you need to get the password value
@Html.EditorFor(m=>m.Password)
}
}
希望这个能对您有所帮助
由于EF无法以这种方式处理循环引用(只能使用主键列进行处理),因此该配置将无法工作。
请在这里看看我的答案
实体与外键交叉引用时的代码优先迁移
我记得前一段时间这样做。 我认为您需要使Person类抽象化。 并让用户继承它。 我认为EF应该自动处理两个表的连接。
这是一个例子:
[Table("Person")]
public abstract class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
[Table("User")]
public class User : Person
{
public string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<User> Users { get; set; }
}
在配置中,我添加了一些User
如下所示:
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(MyContext context)
{
context.Users.AddOrUpdate(
p => p.Name,
new User {Name = "Test User #1"},
new User {Name = "Test User #2"},
new User {Name = "Test User #3"});
}
}
下面是数据库中产生的内容
此处提供了有关EF中模型继承的更多信息
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.