我遇到用户可以拥有多个地址的情况。因此,我的用户类上有一个ICollection。但我也希望用户能够选择默认地址。所以我做了以下事情:
public class User
{
public int Id { get; set; }
public int? DefaultAddressId { get; set; }
[ForeignKey("DefaultAddressId")]
public virtual Address DefaultAddress { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
//properties were removed for purpose of this post
}
我想完全删除public virtual Address DefaultAddress { get; set; }
,保留DefaultAddressId并使用Fluent API映射它,因为当前设置导致了很多麻烦(在这个和其他我有类似设置的类中) 。那么可以使用流畅的api来完成吗?
更新 地址类目前没有对User类的任何引用,它是一种单向关系。但是,是的,一个地址只属于一个用户,它不是一个多对多的关系。这是地址类:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public virtual Area Area { get; set; }
}
答案 0 :(得分:10)
我个人会将外键关系从User
移到Address
,并在地址类上添加IsDefaultAddress
属性。
public class Address
{
public int Id { get; set; }
// This property marks the FK relation
public virtual User User { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public virtual Area Area { get; set; }
// This property signals whether this is the user's default address
public bool IsDefaultAddress { get; set; }
}
EF会知道Foreign Key
和Address
之间需要User
关系。
这会简化您的模型。当然,如果地址只能属于一个用户(如评论中的Slauma所述)。
答案 1 :(得分:8)
问题中的原始模型应该有效。你可以很容易地测试它:
的Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
namespace EFTestApp
{
public class User
{
public int Id { get; set; }
public int? DefaultAddressId { get; set; }
[ForeignKey("DefaultAddressId")]
public virtual Address DefaultAddress { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (var context = new Context())
{
try
{
User user = new User() { Addresses = new List<Address>() };
Address address1 = new Address() { Name = "Address1" };
Address address2 = new Address() { Name = "Address2" };
user.Addresses.Add(address1);
user.Addresses.Add(address2);
context.Users.Add(user);
context.SaveChanges();
// user has now 2 addresses in the DB and no DefaultAddress
user.DefaultAddress = address1;
context.SaveChanges();
// user has now address1 as DefaultAddress
user.DefaultAddress = address2;
context.SaveChanges();
// user has now address2 as DefaultAddress
user.DefaultAddress = null;
context.SaveChanges();
// user has now no DefaultAddress again
}
catch (Exception e)
{
throw;
}
}
}
}
}
在SQL Server Express中,它会创建一个名为“EFTestApp.Context”的新数据库。您可以在上面的每个SaveChanges上设置断点,跳过并观察数据库中的更改。
如果查看数据库中的关系,那么有两个,并且DB中的表Addresses
中有一个外键列User_Id
。
我认为你也可以删除public int? DefaultAddressId { get; set; }
和[ForeignKey("DefaultAddressId")]
。它使用可选的DefaultAddress创建相同的数据库表和关系。
也许您希望根据需要建立关系Address -> User
(如果没有用户,地址不能单独存在于数据库中)。然后,您可以将其添加到Context类:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(u => u.Addresses)
.WithRequired();
}
它使地址表中的User_Id
不可为空,并默认设置级联删除。因此,当用户被删除时,其所有地址也会被删除。
答案 2 :(得分:2)
DefaultAddressId
不需要任何特定的映射,因为它只是User
表中的列而没有与Address
表的任何关系(FK)。将不会创建关系,因为任何一方都不存在导航属性。它也应该是一对一的关系,因为EF不支持唯一键,所以它不起作用。
我喜欢@Sergi Papaseit提供的解决方案
答案 3 :(得分:0)
如果要删除DefaultAddress属性,则无需映射它。您可以在那里拥有该属性,EF应该知道如何映射它,前提是DefaultAddressId在User表中