操作中经常要涉及到模型和值转换的问题,这里记录一下,实际使用过程中遇到过的问题,而非功能的全部。
模型
EFCore中支持字段
参考地址:https://docs.microsoft.com/zh-cn/ef/core/modeling/backing-field?tabs=data-annotations
注意:字段和属性的类型要一致。
按照约定,将发现以下字段作为给定属性的支持字段 (按优先级顺序) 列出。
_<camel-cased property name>
_<property name>
m_<camel-cased property name>
m_<property name>
public class Blog
{
private string _url;
public int BlogId { get; set; }
public string Url
{
get { return _url; }
set { _url = value; }
}
}
构造函数
可以使用参数定义构造函数,并在创建实体实例时使 EF Core 调用此构造函数。 构造函数参数可以绑定到映射的属性或各种类型的服务,以促进延迟加载等行为。
参考地址:https://docs.microsoft.com/zh-cn/ef/core/modeling/constructors
注意:构造函数中的参数与属性的 类型 和 名称 必须相匹配,具体如下:
- 并非所有属性都需要具有构造函数参数。 例如,后期内容属性不是由任何构造函数参数设置的,因此 EF Core 会在以正常方式调用构造函数后对其进行设置。
- 参数类型和名称必须与属性类型和名称相匹配,但在参数采用 camel 大小写格式时,属性可以采用 Pascal 大小写形式。
- EF Core 无法使用构造函数在) 以上的博客或文章 (设置导航属性。
- 构造函数可以是公共的,也可以是私有的,或者具有任何其他可访问性。 不过,延迟加载代理要求构造函数可从继承代理类访问。 通常,这意味着将其设为公共或受保护。
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
值转换
这篇文章主要是想记录值转换,不过涉及模型中像上面的常用方式。
注:复杂的值转换如需要转成对象,或者反序列化之类的,实测在用上面这两种模型构造中,使用转换后赋值是行不通的,上面这两种都要求字段和属性的类型及名称匹配,或者参数和属性的类型和名称要匹配。
所以真正值转换还是要靠ValueConverter 类。
参考地址:https://docs.microsoft.com/zh-cn/ef/core/modeling/value-conversions
ValueConverter类实例化时,有两个主要参数Expression<Func<TModel, TProvider>> convertToProviderExpression和Expression<Func<TProvider, TModel>> convertFromProviderExpression。
Expression<Func<TModel, TProvider>> convertToProviderExpression 这是把 实体模型属性的类型转换为数据库类型的表达式树
Expression<Func<TProvider, TModel>> convertFromProviderExpression 这个是相反的把数据库字段类型转为实体属性类型的表达式树
其一正一反,一个是写库时要把实体属性类型转为表字段类型,一个是读库时,把表字段类型回转为实体模型中属性的类型。
public class Test
{
//通过构造函数方法进行值转换行不通
//public Test(string testName)
//{
// this.TestName = JsonHelper.ToObject<Dictionary<string, string>>(testName);
//}
public int Id { get; set; }
public List<UpgradeWayModel> TestName { get; set; }
}
public class UpgradeWayModel
{
public int Id { get; set; }
public int SourceType { get; set; } = -1;
public int UpgradeType { get; set; }
}
//DbContext
public virtual DbSet<Test> Test { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//把表中字段在存储的序列化字符串反序列化为字典
//var converter = new ValueConverter<Dictionary<string, string>, string>(
//v => JsonHelper.ToJson(v),
//v => JsonHelper.ToObject<Dictionary<string, string>>(v));
//把表中字段在存储的序列化字符串反序列化为对象
var converter = new ValueConverter<List<UpgradeWayModel>, string>(
v => JsonHelper.ToJson(v),
v => JsonHelper.ToObject<List<UpgradeWayModel>>(v));
modelBuilder
.Entity<Test>()
.Property(e => e.TestName)
.HasConversion(converter);
}
也可以对读取到的数据进行统一的加工。如下:
public class NetworkAcessLink
{
public int Id { get; set; }
public string ClassSku { get; set; }
}
//---------------------------------------------------
//DbContext
public virtual DbSet<NetworkAcessLink> NetworkAcessLink { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//对表中读取到的值进行加工
var converter = new ValueConverter<string, string>(v => v, v => string.Concat("0x", v.ToUpper()));
modelBuilder.Entity<NetworkAcessLink>()
.Property(b => b.ClassSku)
.HasConversion(converter);
}