直径4公分红枫树多少钱一棵?

小说:直径4公分红枫树多少钱一棵?作者:龙北石纯更新时间:2019-05-27字数:72928

两个星期前,微软发布了EF Core 2.1 Preview 1,同时还发布了.NET Core 2.1 Preview 1和ASP.NET Core 2.1 Preview 1;EF Core 2.1 Preview 1 除了许多小改进和超过100种产品错误修复之外,还包括几个常用的新功能,今天我为您详细介绍这些新功能的部分内容。

实体构造函数参数

EF.Core 2.1开始支持在实体的构造函数的实体中转入参数,目前支持的类型如下:

  • 实体属性
  • IOC容器中注册的服务
  • 当前的DbContext
  • 当前实体的元数据

实体属性

在某些情况下为了保证数据的安全性,将属性改为只读,在构造函数中传递属性的值,框架通过参数与属性匹配关系,将数据行中属性的值作为参数传递给构造函数。

例如下面的实体:

    public class Order
    {
        public Order(int orderID, string customerID, DateTime? orderDate)
        {
            OrderID = orderID;
            CustomerID = customerID;
            OrderDate = orderDate;
        }

        public int OrderID { get; }
        
        public string CustomerID { get; }

        public DateTime? OrderDate { get; }

    }

其中参数与属性的配置规则如下:

  • 参数的类型与属性的类型一致;
  • 属性名与参数名除首字母不区分大小写之外,其它字符一致,并且可以使用 _m_做为前缀,使用OrderID属性来举例,存在如下匹配规则:

    属性名 参数名
    OrderID OrderID
    OrderID orderID
    _OrderID orderID
    _OrderID OrderID
    m_OrderID OrderID
    m_OrderID OrderID

具体的匹配规则可以见Github上面的源代码:https://github.com/aspnet/EntityFrameworkCore/blob/8965f0b91cf89e36abca8636d58420cbd26c22fd/src/EFCore/Metadata/Internal/PropertyParameterBindingFactory.cs#L37-L45
不过我认识后面四种模式有待斟酌的,在.Net开发规范,应该没有人将公有的属性名使用 _m_作为前缀。

IOC容器中注册的服务

在实体的构造函数的中,可以将注册的服务作为参数。

示例代码:

    public class Order
    {
        private ILazyLoader _lazyLoader;

        public Order(ILazyLoader lazyLoader)
        {
            this._lazyLoader = lazyLoader;
        }

        public int OrderID { get; set; }
        
        public string CustomerID { get; set; }

        private ICollection<OrderDetail> _orderDetails;

        public ICollection<OrderDetail> OrderDetails
        {
            get => _lazyLoader.Load(this, ref _orderDetails);
            set => _orderDetails = value;
        }
    }

}

其中ILazyLoader是EF Core框架在容器中注册的一个服务,通过实体的构造函数中传入,实现导航属性的赖加载(关于ILazyLoader的具体使用方式在本章的下一节中讲解)。

当前的DbContext

在实体的构造函数的参数中,将当前的DbContext作为参数。

示例代码:

    public class Order
    {
        private NorthwindContext _northwindContext;

        public Order(NorthwindContext northwindContext)
        {
            this._northwindContext = northwindContext;
        }

        public int OrderID { get; set; }
        
        public string CustomerID { get; set; }

        private ICollection<OrderDetail> _orderDetails;

        [NotMapped]
        public ICollection<OrderDetail> OrderDetails
        {
            get
            {
                if (this._orderDetails == null)
                    this._orderDetails = this._northwindContext.Set<OrderDetail>()
                        .Where(item => item.OrderID == this.OrderID).ToList();
                return this._orderDetails;
            }
            set => _orderDetails = value;
        }
    }

当前实体的元数据

在实体的构造函数的参数中,将当前实体的的IEntityType作为参数。

示例代码:

    public class Order
    {

        private IEntityType _entityType;

        public Order(IEntityType entityType)
        {
            this._entityType = entityType;
        }

        public int OrderID { get; set; }
        
        public string CustomerID { get; set; }

        [NotMapped]
        public IEntityType EntityType
        {
            get { return this._entityType; }
        }

    }

如果实体存在多个构造函数,框架会选择参数个数最多的那个;如果按参数个数优先选择后,依然存在多个构造函数,则会抛异常。在当前体验版本中,暂时无法直接支持自定义参数,不过在下一个发布版本中,会提供解决方案。

懒加载

懒加载是一个非常有争论的功能激烈争论的功能。虽然有些人认为它会导致性能下降或出现意想不到的Bug,但是不影响有些开发人员依旧喜欢它。EF Core 2.1 Preview 1增加了懒加载,提供了两种实现方式。

使用ILazyLoader接口实现懒加载

在实体的构造函数中传入ILazyLoader,在导航属性中,使用接口的Load方法,实现导航属性的数据加载。

示例代码:

    public class Order
    {
        private ILazyLoader _lazyLoader;


        public Order(ILazyLoader lazyLoader)
        {
            this._lazyLoader = lazyLoader;
        }

        public int OrderID { get; set; }
        
        public string CustomerID { get; set; }

        public DateTime? OrderDate { get; set; }
   
        private ICollection<OrderDetail> _orderDetails;

        public ICollection<OrderDetail> OrderDetails
        {
            get => this._lazyLoader.Load(this, ref _orderDetails);
            set => _orderDetails = value;
        }
    }

通过代理类实现懒加载

这种方式,需要单独安装 Microsoft.EntityFrameworkCore.Proxies Nuget 包,它通过 Castle.Core 框架来生成代理类来实现对导航属性的延迟加载。

启用懒加载需要注意以下两点:

  • 在配置中启用懒加载;
  • 实体类不能是封闭(sealed)类,导航属性必须是虚(virtual)属性。

这种方式,在以前的博客我已经分享过,只不过当时还没有发布,原文地址:Entity Framework Core 懒加载。

值转换

EF Core 2.1 允许您将插入数据库的值自定义转换逻辑。例如:将属性的值进行加密与解密。

示例,将插入的值进行Base64编码,在查询的时候进行Base64解码。

定义的UserInfo实体,用于保存用户信息,属性PhoneNumber表示用户的手机号码;为了用户信息安全,需要将手机号码进行加密后再保存到数据库,只是为了达到演示的目的,我们采用Base64进行编码。

     public class UserInfo
     {
         public int Id { get; set; }

         public string PhoneNumber { get; set; }
     }

Base64ValueConverter表示进行值转换的具体逻辑,继承自泛型ValueConverter<string, string>,具体的逻辑非常简单,不再叙述。

    public class Base64ValueConverter : ValueConverter<string, string>
    {
        public Base64ValueConverter() : base((v) => ToBase64(v), (v) => FromBase64(v))
        {
        }
        private static string ToBase64(string input)
        {
            if (string.IsNullOrEmpty(input))
                return input;

            var bytes = Encoding.UTF8.GetBytes(input);
            return Convert.ToBase64String(bytes);
        }

        private static string FromBase64(string input)
        {
            if (string.IsNullOrEmpty(input))
                return input;

            var bytes = Convert.FromBase64String(input);
            return Encoding.UTF8.GetString(bytes);
        }
    }

SampleDbContext表示数据上下文,在OnModelCreating方法中,定义UserInfo实体的PhoneNumber属性需要使用Base64进行值转换。

    public class SampleDbContext : DbContext
    {

        public DbSet<UserInfo> Users { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var sqlConnectionStringBuilder = new SqlConnectionStringBuilder
            {
                DataSource = "*******",
                InitialCatalog = "ValueConverterTest",
                UserID = "sa",
                Password = "sa"
            };
            optionsBuilder.UseSqlServer(sqlConnectionStringBuilder.ConnectionString);

            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<UserInfo>().Property(e => e.PhoneNumber).HasConversion(new Base64ValueConverter());
        }
    }

下面的代码是对预期的结果进行单测。

    [Fact]
    public async void ValueConverter_Test()
    {
        string phoneNumber = "13658556925";

        using (SampleDbContext dbContext = new SampleDbContext())
        {
            await dbContext.Database.EnsureDeletedAsync();

            await dbContext.Database.EnsureCreatedAsync();

            dbContext.Users.Add(new UserInfo()
            {
                PhoneNumber = phoneNumber
            });

            await dbContext.SaveChangesAsync();
        }

        UserInfo user;

        using (SampleDbContext dbContext = new SampleDbContext())
        {
            user = dbContext.Users.Single();
        }

        Assert.NotNull(user);
        Assert.Equal(phoneNumber, user.PhoneNumber);
    }

运行后,查询数据库中保存的结果:

手机号码 13658556925 在数据库保存的值是 MTM2NTg1NTY5MjU=

使用值转换的另一个常用场景是将枚举的值存储为字符串类型,默认情况下,枚举的值保存到数据库中是通过整数表示的,如果需要在值存储为字符串类型。

   public enum CategoryName
   {
       Clothing,
       Footwear,
       Accessories
   }
   public class Category
   {
       public int Id { get; set; }
 
       public CategoryName Name { get; set; }
   }

实体CategoryName属性是用枚举表示的,如果在存储时用字符串类型表示,我们可以在DbContextOnModelCreating方法中使用如下代码,

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>().Property(e => e.Name).HasConversion<string>();
    }

EF Core 默认提供常用类型的转换,我们只需指定存储的类型即可,框架默认支持的类型转换映射表如下:

源类型 目标类型
enum intshortlongsbyteuintushortulongbytedecimaldoublefloat
bool intshortlongsbyteuintushortulongbytedecimaldoublefloat
bool string
bool byte[]
char string
char intshortlongsbyteuintushortulongbytedecimaldoublefloat
char byte[]
Guid byte[]
Guid string
byte[] string
string byte[]
DateTimeDateTimeOffsetTimeSpan stringlongbyte[]
intshortlongsbyteuintushortulongbytedecimaldoublefloat stringbyte[]

LINQ GroupBy 解析

在版本2.1之前,在EF Core中,GroupBy 表达式运算符总是在内存中进行计算的。现在支持在大多数情况下将其转换为SQL GROUP BY子句。

var query = context.Orders
    .GroupBy(o => new { o.CustomerId, o.EmployeeId })
    .Select(g => new
        {
          g.Key.CustomerId,
          g.Key.EmployeeId,
          Sum = g.Sum(o => o.Amount),
          Min = g.Min(o => o.Amount),
          Max = g.Max(o => o.Amount),
          Avg = g.Average(o => Amount)
        });

相应的SQL解析如下所示:

SELECT [o].[CustomerId], [o].[EmployeeId],
    SUM([o].[Amount]), MIN([o].[Amount]), MAX([o].[Amount]), AVG([o].[Amount])
FROM [Orders] AS [o]
GROUP BY [o].[CustomerId], [o].[EmployeeId];

查询类型

EF Core 模型现在可以包含查询类型。与实体类型不同,查询类型没有定义主键,也不能插入、删除或更新操作(即它们是只读的),但它们可以直接由查询返回。查询类型的一些使用场景:

  • 映射到没有主键的视图
  • 映射到没有主键的表
  • 映射到模型中定义的查询
  • 作为FromSql()查询的返回类型

示例,定义一个简单的BlogPost模型:

    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public ICollection<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int BlogId { get; set; }
    }

定义一个简单的数据库视图,能够查询每博客与文章数:

    db.Database.ExecuteSqlCommand(
        @"CREATE VIEW View_BlogPostCounts AS 
            SELECT Name, Count(p.PostId) as PostCount from Blogs b
            JOIN Posts p on p.BlogId = b.BlogId
            GROUP BY b.Name");

定义一个类映射的数据库视图的结果:

    public class BlogPostsCount
    {
        public string BlogName { get; set; }
        public int PostCount { get; set; }
    }

DbContext类的OnModelCreating使用modelBuilder.Query<T>API。 我们可以使用标准 fluent 配置 Api 来配置查询类型的映射:

    public class SampleDbContext : DbContext
    {
        public DbQuery<BlogPostsCount> BlogPostCounts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder
                .Query<BlogPostsCount>().ToTable("View_BlogPostCounts")
                .Property(v => v.BlogName).HasColumnName("Name");
        }
    }

查询数据库视图中的标准方式:

    var postCounts = db.BlogPostCounts.ToList();
    
    foreach (var postCount in postCounts)
    {
        Console.WriteLine($"{postCount.BlogName} has {postCount.PostCount} posts.");
        Console.WriteLine();
    }

最后

EF Core 2.1 Preview1 新增功能的部分内容已经介绍完了,希望对您有帮助。如果文章中描述的功能存在遗漏或错误,请在评论中留言,谢谢!

当前文章:http://780pk.com/array/hzbjgdy3kq.html

发布时间:2019-05-27 07:40:25

三月份可以移栽睡莲吗? 哪里有卖欧月的扦插苗? 内蒙古可以栽植麦冬草吗? 同样都是刺柏,但是我这里的刺柏只是精品,价格还便宜 【机密文件】水腊苗价格内部批发报价清单! 2016下半年最新水生美人蕉价格是多少钱一棵? 樱花多少钱能买到? 桂花种子几月播种最好? 虞美人秋天种植方法是什么? 去哪里可以买到提木西草种子呢?

54768 19043 43119 12528 24352 90510 36625 38389 75672 91139 15546 24727 11544 71101 31929 23026 15364 38378 91177 77660 24926 73873 22457

我要说两句: (0人参与)

发布