asp.net core系列 28 EF模型配置(字段,构造函数,拥有实体类型)

一. 支持字段

EF允许读取或写入字段而不是一个属性。在使用实体类时,用面向对象的封装来限制或增强应用程序代码对数据访问的语义时,这可能很有用。无法使用数据注释配置。除了约定,还可以使用Fluent API为属性配置支持字段。

1.1 约定
public class Blog
{
   // _<camel-cased property name>
    private string _url;

    public int BlogId { get; set; }

    public string Url
    {
        get { return _url; }
        set { _url = value; }
    }
}
1.2 Fluent API
modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .HasField("_validatedUrl");

public class Blog
{
    private string _validatedUrl;
   
    public string Url
    {
        get { return _validatedUrl; }
    }

    public void SetUrl(string url)
    {
       //...
        _validatedUrl = url;
    }
}

二. 构造函数

从开始 EF Core 2.1,可以定义带参数的构造函数,并在创建实体实例时让EF Core调用此构造函数。构造函数参数可以绑定到映射属性,或绑定到各种服务,以方便延迟加载等行为。

2.1 带参的构造函数

下面代码演示带参数的构造函数,并且设置只读属性,外部调用该类时,只能通过构造函数传入实体值。

public class Blog
{
    public Blog(int id, string name, string author)
    {
        Id = id;
        Name = name;
        Author = author;
    }

    public int Id { get; private set; }

    public string Name { get; private set; }
    public string Author { get; private set; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

别外使用私有setter的另一种方法是使属性真正只读,并在OnModelCreating中添加更明确的映射。

public class Blog
{
    private int _id;

    public Blog(string name, string author)
    {
        Name = name;
        Author = author;
    }

    public string Name { get; }
    public string Author { get; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        b =>
        {
            b.HasKey("_id");
            b.Property(e => e.Author);
            b.Property(e => e.Name);
        });
}
2.2 注入服务

EF Core还可以将“服务”注入实体类型的构造函数中。例如,可以注入以下内容:

       DbContext - 当前上下文实例,也可以作为派生的DbContext类型键入

       ILazyLoader- 延迟加载服务

       Action<object, string>- 一个延迟加载的委托

       IEntityType - 与此实体类型关联的EF Core元数据

例如,注入的DbContext可用于选择性地访问数据库以获得关于相关实体的信息而无需全部加载它们。在下面的示例中,这用于获取Blog博客中的Posts帖子数量:

public class Blog
{
    public Blog()
    {
    }

    private Blog(BloggingContext context)
    {
        Context = context;
    }

    private BloggingContext Context { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Post> Posts { get; set; }

    //获取帖子数量
    public int PostsCount
        => Posts?.Count
           ?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
           ?? 0;
}

有一些需要注意:

       (1)构造函数是私有的,因为它只由EF Core调用,并且还有另一个通用的公共构造函数。

(2)使用注入服务的代码(即EF上下文)防御它为null,处理EF Core未创建实例的情况。

(3)因为服务存储在读或写属性中,所以当实体附加到新的上下文实例时,它将被重置。

三.拥有的实体类型

该功能是在 EF Core 2.0 中的新增功能。是指:当一个实体类中包含导航属性(实体类型引用属性),并对导航属性进行建模,这个导航属性类被称为“拥有实体类型”。而包含“拥有实体类型”的类叫:所有者。

3.1 显示配置

EF Core中的所有实体类型永远不会按照约定包含在模型中。可以使用OwnsOne在(使用EF Core 2.1中的新增功能)OnModelCreating中使用或用注释类型OwnedAttribute将类型配置为拥有类型。

下面示例中StreetAddress是一个没有标识属性的类型。 它用作 Order 类型的属性来指定特定订单的发货地址。

//拥有实体类型
 [Owned]
public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

//所有者
public class Order
{
    public int Id { get; set; }

    public StreetAddress ShippingAddress { get; set; }
}

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
    public DbSet<Order> Order { get; set; }
}

使用EF基于数据模型(Order)创建数据库,如下图所示。


image

 还可以使用该OwnsOne方法在OnModelCreating中指定ShippingAddress属性,是Order实体类型的拥有实体,并根据需要配置其他方面。

modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);

如果ShippingAddress属性在Order实体中为私有属性,则可以使用的字符串版本OwnsOne方法:

modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");
3.2 隐含键

OwnsOne通过引用导航配置或发现的拥有类型始终与所有者具有一对一的关系,因此拥有实体不需要自己的键值,因为外键值是唯一的。在前面的示例中,StreetAddress类型不需要定义键属性。拥有实体类型的实例键值与所有者实例的键值相同。

3.3 拥有的集合类型

要配置拥有的集合类型,使用OwnsMany在OnModelCreating中使用。但是,主键不会按约定配置,因此需要明确指定。下面代码演示拥有的集合类型的关键代码。

public class Distributor
    {
        public int Id { get; set; }
        public ICollection<StreetAddress> ShippingCenters { get; set; }
    }


   modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
            {
                // 给ShippingCenters表设置一个主键
                a.Property<int>("Id");
                //给ShippingCenters表设置一个外键
                a.HasForeignKey("DistributorId");
                //设置复合主键
                a.HasKey("DistributorId", "Id");
            });

使用EF基于数据模型(Distributor)创建数据库,如下图所示(一对多的关系)。Distributor_ShippingCenters表的ID为主键,DistributorId为外键。


image

 设计限制:

         对于拥有的实体类型无法创建DbSet<T>。

         在 ModelBuilder中调用Entity<T>()时,T不能是拥有的实体类型。

关于拥有的实体类型的更多介绍参考官方文档,这里不是介绍。关于“EF模型配置系列”的很多功能,都是基于code first模式。

参考文献:

官方文档:EF支持字段

EF构造函数

EF拥有的实体类型

拥有实体类型的完整案例

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容