1.索引

索引是跨多个数据存储区的常见概念。尽管它们在数据存储中的实现可能会有所不同,但也可用于基于列(或一组列)更高效地进行查找。

1.1约定

按照约定,将在用作外键的每个属性(或一组属性)中创建索引。

1.2数据批注

不能使用数据批注创建索引。

1.3fluent api

您可以使用熟知的api来指定单个属性的索引。默认情况下,索引不是唯一的。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<blog>()
            //配置索引
            .hasindex(b => b.url);
    }
}
public class blog
{
    public int blogid { get; set; }
    public string url { get; set; }
}

您还可以指定索引应是唯一的,这意味着对于给定的属性,不能有两个实体具有相同的值。

modelbuilder.entity<blog>()
.hasindex(b => b.url)
.isunique();

您还可以为多个列指定索引。

class mycontext : dbcontext
{
    public dbset<person> people { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<person>()
            .hasindex(p => new { p.firstname, p.lastname });
    }
}
public class person
{
    public int personid { get; set; }
    public string firstname { get; set; }
    public string lastname { get; set; }
}

2.备用键

备用键与主键相对,用作每个实体实例的备用唯一标识符。备用键可用作关系的目标。使用关系数据库时,这将映射到备用键列上的唯一索引/约束和引用列的一个或多个外键约束的概念。系统通常会在需要时为你引入备用键,你无需手动配置它们。

2.1约定

按照约定,系统将在识别属性(不是主键)时为你引入备用键,充当关系的目标。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    public dbset<post> posts { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<post>()
            .hasone(p => p.blog)
            .withmany(b => b.posts)
            .hasforeignkey(p => p.blogurl)
            .hasprincipalkey(b => b.url);
    }
}
public class blog
{
    public int blogid { get; set; }
    public string url { get; set; }
    
    public list<post> posts { get; set; }
}
public class post
{
    public int postid { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    //备用键
    public string blogurl { get; set; }
    public blog blog { get; set; }
}

2.2数据注释

不能使用数据注释配置备用键。

2.3fluent api

你可以使用熟知的api将单个属性配置为备用密钥。

class mycontext : dbcontext
{
    public dbset<car> cars { get; set; }

    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<car>()
            //配置为备用密钥
            .hasalternatekey(c => c.licenseplate);
    }
}
class car
{
    public int carid { get; set; }
    public string licenseplate { get; set; }
    public string make { get; set; }
    public string model { get; set; }
}
你还可以使用熟知的api将多个属性配置为备用密钥(称为复合备用键)。
class mycontext : dbcontext
{
    public dbset<car> cars { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<car>()
            //配置为备用密钥
            .hasalternatekey(c => new { c.state, c.licenseplate });
    }
}
class car
{
    public int carid { get; set; }
    public string state { get; set; }
    public string licenseplate { get; set; }
    public string make { get; set; }
    public string model { get; set; }
}

3.继承

ef模型中的继承用于控制如何在数据库中表示实体类中的继承。

3.1约定

按照约定,由数据库提供商确定如何在数据库中表示继承。有关如何使用关系数据库提供程序来处理此情况的详细说明。如果模型中显式包括两个或更多个继承类型,则ef仅会设置继承。ef不会扫描模型中未包含的基类型或派生类型。可以通过为继承层次结构中的每个类型公开dbset,在模型中包含类型。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    public dbset<rssblog> rssblogs { get; set; }
}
public class blog
{
    public int blogid { get; set; }
    public string url { get; set; }
}
public class rssblog : blog
{
    public string rssurl { get; set; }
}

如果不想公开层次结构中一个或多个实体的dbset,可以使用熟知的api来确保它们包含在模型中。如果不依赖约定,则可以使用hasbasetype显式指定基类型。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<rssblog>().hasbasetype<blog>();
    }
}

4.支持字段

支持字段允许ef读取和写入字段,而不是属性。当使用类中的封装来限制或通过应用程序代码对数据访问进行限制时,这可能很有用,但在不使用这些限制的情况下,应从数据库中读取或写入值。

4.1约定

按照约定,将发现以下字段作为给定属性的支持字段(按优先级顺序列出)。仅为模型中包含的属性发现字段。

public class blog
{
    private string _url;
    public int blogid { get; set; }
    public string url
    {
        get { return _url; }
        set { _url = value; }
    }
}

配置了支持字段后,当从数据库具体化实体实例(而不是使用属性资源库)时,ef将直接写入该字段。如果ef需要在其他时间读取或写入值,则它将使用属性(如果可能)。例如,如果ef需要更新某个属性的值,则它将使用属性setter(如果已定义)。如果该属性为只读,则它将写入字段。

4.2数据注释

不能通过数据批注配置支持字段。

4.3fluent api

你可以使用熟知的api来配置属性的支持字段。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<blog>()
            .property(b => b.url)
            .hasfield("_validatedurl");
    }
}
public class blog
{
    private string _validatedurl;
    public int blogid { get; set; }
    public string url
    {
        get { return _validatedurl; }
    }
    public void seturl(string url)
    {
        using (var client = new httpclient())
        {
            var response = client.getasync(url).result;
            response.ensuresuccessstatuscode();
        }
        _validatedurl = url;
    }
}
4.3.1控制何时使用字段

可以配置ef何时使用字段或属性。有关支持的选项,请参阅propertyaccessmode枚举。

modelbuilder.entity<blog>()
    .property(b => b.url)
    .hasfield("_validatedurl")
    .usepropertyaccessmode(propertyaccessmode.field);
4.3.2没有属性的字段

你还可以在你的模型中创建一个概念属性,该属性在实体类中不具有相应的clr属性,而是使用字段来存储实体中的数据。这不同于阴影属性,其中的数据存储在更改跟踪器中。如果实体类使用方法获取或设置值,通常会使用此方法。可以在property(…) api中为ef指定字段的名称。如果没有具有给定名称的属性,则ef将查找字段。

class mycontext : dbcontext
{
    public dbset<blog> blogs { get; set; }
    protected override void onmodelcreating(modelbuilder modelbuilder)
    {
        modelbuilder.entity<blog>()
            .property("_validatedurl");
    }
}
public class blog
{
    private string _validatedurl;
    public int blogid { get; set; }
    public string geturl()
    {
        return _validatedurl;
    }
    public void seturl(string url)
    {
        using (var client = new httpclient())
        {
            var response = client.getasync(url).result;
            response.ensuresuccessstatuscode();
        }
        _validatedurl = url;
    }
}

您还可以选择为属性指定名称,而不是字段名称。然后,在创建模型时使用此名称,最值得注意的是,该名称将用于在数据库中映射到的列名称。

protected override void onmodelcreating(modelbuilder modelbuilder)
{
    modelbuilder.entity<blog>()
        .property<string>("url")
        .hasfield("_validatedurl");
}

如果实体类中没有属性,则可以在linq查询中使用ef.property(…)方法来引用概念上是模型的一部分的属性。

var blogs = db.blogs.orderby(b => ef.property<string>(b, "url"));

参考文献: