zoukankan      html  css  js  c++  java
  • EntityFramework Core 1.1有哪些新特性呢?我们需要知道

    前言

    在项目中用到EntityFramework Core都是现学现用,及时发现问题及时测试,私下利用休闲时间也会去学习其他未曾遇到过或者用过的特性,本节我们来讲讲在EntityFramework Core 1.1中出现了哪些新特性供我们使用。

    EntityFramework Core 1.1新特性探讨

    DbSet.Find

    在EF 6.x中也有此方法的实现,在EF Core 1.1中也同样对此方法进行了实现,为什么要拿出来讲呢,当然也有其道理,我们一起来看看。在仓储中我们实现Find这个方法,如下:

            public virtual T Find(int Key)
            {
                return _context.Set<T>().Find(Key);
            }

    此时我们来查询Blog中主键等于1的数据。

        var blog1 = _blogRepository.Find(1);

    此时我们通过SQL Server Profiler监控得到如下SQL。

    我们看到通过Find方法来查询主键等于1的数据时,声明了一个变量然后再来进行设置变量值进行查询,没毛病,上述我们是直接通过Find方法来实现,下面我们通过其他几种方法来实现。如下:

            public T GetSingle(int id)
            {
                return _context.Set<T>().FirstOrDefault(x => x.Id == id);
            }
     var blog = _blogRepository.GetSingle(1);

    此时和上述Find方法执行的SQL无任何区别,我们先别着急下结论,我们再来通过lambda表达式来实现看看。

            public T GetSingle(Expression<Func<T, bool>> predicate)
            {
                return _context.Set<T>().FirstOrDefault(predicate);
            }
     var blog = _blogRepository.GetSingle(d => d.Id == 1);

    此时我们再来看看生成的SQL语句。

    此时生成的SQL语句没有声明变量看起来非常清爽,同时看过dudu老大刚不久写过在EF Core中我们声明的的lambda表达式中的参数就是我们查询表的别名,确实是如此,不知道你发现了没有。既然以上有多种实现且利用lambda表达式实现更加清爽,那么为何还要搞出一个Find方法呢,请继续往下看。

      var blog = _blogRepository.GetSingle(d => d.Id == 1);
      var blog1 = _blogRepository.Find(1);

    当我们第一次查询了主键等于1的数据时,我们第二次通过Find方法再来进行查询时通过监控SQL Server Profiler,你会发现并未生成任何SQL语句,这说明什么呢,说明EF Core团队给出Find方法的目的在于:当实体已经被加载到上下文中时,我们通过Find方法再去查询数据时此时不会再去数据库中进行查询。所以当我们利用主键查询数据时利用Find方法会减少对数据库的多次请求。

    ICollection<T>(集合类型映射支持)

    在之前EF版本中我们都是进行如下声明字段

        public class Blog : IEntityBase
        {
            public virtual int Id { get; set; }
            public virtual string Name { get; set; }
            public virtual string Url { get; set; }
            public virtual ICollection<Post> Posts { get; set; }
        }

    我们知道在EF Core中已经不存在延迟加载这一概念,所以请用了EF Core的童鞋将virtual关键字去掉。同时我们在映射集合时一直以来都统一用的ICollection<T>,但是在EF Core中不再有此局限性,我们进行如下定义:

        public class Blog : IEntityBase
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Url { get; set; }
            public IEnumerable<Post> Posts { get; set; }
        }

     

    通过如上我们知道现在支持了IEnumerable<T>集合的映射。当然这里有个前提,其具体集合类必须实现ICollection接口,否则EntityFramework Core将无法进行填充。

    Mapping to Fileds(映射到字段)

    这个特性应该是前所未有,只有在EF Core 1.1中才出现,我们详细讲解下Backing Fileds(我们暂且将其翻译为返回字段)特性。自从有了如下自动属性的出现,就方便了我们许多。

     public string Url { get; set; }

    什么是返回字段(Backing Fileds)特性,我们先看下原始为字段配置属性的情况如下:

    private string _url;
    
    public string Url
    {
         get { return _url; }
         set { _url = value; }
    }

    Backing Fileds特性允许EF Core读或者写数据到字段中而不是属性中。也就是说如上EF Core将数据读写到_url字段中而不是Url中。默认情况下满足以下四种规则都会配置成Backing Fileds。

    • _<camel-cased property name>
    • _<property name>
    • m_<camel-cased property name>
    • m_<property name>

    比如属性为UserName,那么对应的Backing Fileds则依次是:_userName,_UserName,m_userName,m_UserName。配置Backing Fileds后,当从数据库查询类实例后将直接将其对应数据写到字段中,在其他时候当EF Core需要读或者写值时有可能使用属性,例如EF需要更新一个属性上的值时,此时将使用属性的set访问器,如果属性仅仅只是只读,那么将值写到字段中。例如如下配置Backing Fileds即_validateUrl。

    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;
        }
    }

    我们也可以在映射中配置使用属性还是字段,如下:

    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .HasField("_validatedUrl")
        .UsePropertyAccessMode(PropertyAccessMode.Field);

    若我们在实体中没有属性,此时我们可以通过字段来存储数据。我们通过映射中的Porperty(...)来指定字段名称,若没有属性,此时EF Core将会查找字段,如下:

    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;
        }
    }

    讲了这么多Backing Fileds特性,不知道看到本篇文章的你清楚了它的作用是什么,为什么要提出Backing Fileds特性,它存在的价值或者说用途是做什么呢,就我个人的理解的话,提出Backing Fileds的多数场景在:如果属性只读,我们需要通过其他逻辑操作来获取其值,但是我们没有一个桥梁来赋予其值,此时我们就需要Backing Fileds来完成。希望看到此文的你有更多见解的话,请留下评论,一起探讨。这里我们结合上述IEnumerable<T>来进一步讲解Backing Fileds。我们在Blog类中是如下定义。

        public class Blog : IEntityBase
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Url { get; set; }
            public IEnumerable<Post> Posts { get; set; }
        }

    我们知道对于导航属性Posts更多的是通过Inlcude来查询出Posts,所以在这里我们完全不需要set访问器以便减少对Posts反编译为Set方法,我们完全可以改造如下:

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

    话又说回来了,如果我们万一需要对Post进行一些操作,那么在这种情况下该如何是好呢,此时我们通过暴露IEnumerable<Blog>导航属性,然后借助该导航属性的Backing Fileds来对Post进行操作,改造如下:

        public class Blog : IEntityBase
        {
            private readonly List<Post> _posts = new List<Post>();
            public int Id { get; set; }
            public string Name { get; set; }
            public string Url { get; set; }
            public IEnumerable<Post> Posts => _posts;
            public void AddPost(Post post)
            {
                // Do some buisness your logic 
                _posts.Add(post);
            }
        }

    我们实际来操作一下,查询Blog数据以及导航属性Post数据。

            public virtual IEnumerable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
            {
                IQueryable<T> query = _context.Set<T>();
                foreach (var includeProperty in includeProperties)
                {
                    query = query.Include(includeProperty);
                }
                return query.AsEnumerable();
            }

    我们进行如下查询:

     var blog = _blogRepository.GetSingle(d => d.Id == 1, d => d.Posts);

    我们上述稍微改造了一下,为了以免查询出现错误,测试查询一下,如下,没毛病。

     

    显式加载(Explicit Loading) 

    貌似显式加载没有什么应用的场景,不知道是否是应对某些特定的场景而给,它只是加载被上下文跟踪实体的导航属性,通过Include我们也可以实现,如下:

                var blog = _efCoreContext.Set<Blog>().Find(1);
                _efCoreContext.Entry(blog).Collection(b => b.Posts).Load();
                _efCoreContext.Entry(blog).Reference(b => b.Posts).Load();

    连接弹性(Connection resiliency) 

    所谓的连接弹性则是执行数据库命令失败时我们可以重试,我们可以在OnConfiguring或者Startup.cs中设置,如下:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(
                "connection string",
                options => options.EnableRetryOnFailure());
    }

    SQL Server内存优化表支持

    内存优化表是SQL Server的一个特性,它将整个表驻留在内存中,在磁盘上保留着对表的副本,主要是用于持久化,在数据库恢复时(比如重启)在内存优化表中的数据从磁盘上仅仅只是进行读取。比如对Blog表进行内存优化设置,如下:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .ForSqlServerIsMemoryOptimized();
    }

    将实体映射到内存优化中的表,当使用EF Core基于我们的模型创建数据库时,此时这些实体也将在内存优化表中创建一份。

    简化服务更换(Simplify switch services)

    在EF Core 1.0中就可以实现服务更换,但是略显复杂,在EF Core 1.1中替换服务类似于依赖注入一样,如下:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("connection string");
    
        optionsBuilder.ReplaceService<SqlServerTypeMapper, MyCustomSqlServerTypeMapper>();
    }

    在EF 6.x之前版本中因为导航属性的存在很容易导致循环引用,所以对于EF Core同样是如此我们需要在Startup.cs中忽略循环引用,如下:

     services.AddMvc()
            .AddJsonOptions(
                options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
            );

    总结

    本节我们比较详细的讲解了EF Core 1.1中新添加或改善的特性,我们重点讲述了Backing Fileds特性。

  • 相关阅读:
    Adodb.recorset中Field字段的属性及意义
    郁闷的新一篇
    最近身体越来越差了。。。
    成事在天,谋事在人
    我要带我老婆去看看机场...
    Javascript 时间比较
    同步异步传输
    使用WSPBuilder 生成wsp文件,部署,激活,使用
    动态添加SqlParameter
    C#几种排序方法
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/6308859.html
Copyright © 2011-2022 走看看