代码之家  ›  专栏  ›  技术社区  ›  Steve Harris

有没有一种简单的方法可以使用数据注释或自定义类型,将存储在SQL中的值用作EF中的DateTime?

  •  -1
  • Steve Harris  · 技术社区  · 6 年前

    我有一个数据库,其中所有日期和时间分别存储为字符串[yyyyMMdd]和[hhmms]。

    我是否可以在POCO类中添加一个数据注释,以便将其识别为应该是的类型?

    如:

    [Column(TypeName="varchar(8)", Format="yyyyMMdd"] // Format does not exist!
    public DateTime MyDate { get; set; }
    

    注: 我不想使用私有和公共属性对来转换这个客户端。我希望基本查询转到数据库,这将阻止。

    这里有一个类似的问题,我不想得到答案: convert-value-when-mapping

    2 回复  |  直到 6 年前
        1
  •  2
  •   Steve Harris    6 年前

    好吧,我已经通过下载和调查EF源代码找到了答案。有几个类需要重写。这在我使用的EF Core 2.1.2中有效,因此不能保证旧版本或更新版本(因为API声明这些类可能会被更改),但如果有问题的话,希望只有很小的更改。

    需要一个类在字符串和日期之间进行转换:

    public class StringDateConverter : ValueConverter<DateTime?, string>
    {
        // these can be overridden
        public static string StringDateStorageType = "char(8)";
        public static string StringDateStorageFormat = "yyyyMMdd";
        public static string StringDateEmptyValue = "00000000";
    
        protected static readonly ConverterMappingHints _defaultHints
            = new ConverterMappingHints(size: 48);
    
        public StringDateConverter()
            : base(ToString(), ToDateTime(), _defaultHints)
        {
        }
    
        protected new static Expression<Func<DateTime?, string>> ToString()
            => v => DateToString(v);
    
        protected static Expression<Func<string, DateTime?>> ToDateTime()
            => v => StringToDate(v);
    
        private static string DateToString(DateTime? date)
        {
            if (date.HasValue)
                return date.Value.ToString(StringDateStorageFormat);
    
            return StringDateEmptyValue;
        }
    
        private static DateTime? StringToDate(string date)
        {
            if (!string.IsNullOrWhiteSpace(date) 
                && !(date == StringDateEmptyValue)
                && DateTime.TryParseExact(date, StringDateStorageFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime result))
                return result;
    
            return null;
        }
    }
    

    这个类继承EF SqlServerDateTimeTypeMapping并使用上面的转换器。

    public class SqlServerDateTypeMapping : Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDateTimeTypeMapping
    {
        public SqlServerDateTypeMapping() 
            : this(StringDateConverter.StringDateStorageType, System.Data.DbType.String)
        {
        }
    
        public SqlServerDateTypeMapping(string storeType, DbType? dbType = null) 
            : base(storeType, dbType)
        {
        }
    
        protected SqlServerDateTypeMapping(RelationalTypeMappingParameters parameters)
            : base(parameters)
        {
        }
    
        public override DbType? DbType => System.Data.DbType.String;
    
        protected override string SqlLiteralFormatString
            => StoreType == StringDateConverter.StringDateStorageType
                ? "'" + StringDateConverter.StringDateStorageFormat + "'"
                : base.SqlLiteralFormatString;
    
        public override ValueConverter Converter => new StringDateConverter();
    
        // ensure cloning returns an instance of this class
    
        public override RelationalTypeMapping Clone(in RelationalTypeMappingInfo mappingInfo)
        {
            return new SqlServerDateTypeMapping();
        }
    
        public override RelationalTypeMapping Clone(string storeType, int? size)
        {
            return new SqlServerDateTypeMapping();
        }
    
        public override CoreTypeMapping Clone(ValueConverter converter)
        {
            return new SqlServerDateTypeMapping();
        }
    }
    

    public class SqlServerTypeMappingSource : Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerTypeMappingSource
    {
        public SqlServerTypeMappingSource(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies) : base(dependencies, relationalDependencies)
        {
        }
    
        protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
        {
            if (mappingInfo.ClrType == typeof(DateTime) && mappingInfo.StoreTypeName == StringDateConverter.StringDateStorageType)
                return new SqlServerDateTypeMapping();
    
            return base.FindMapping(mappingInfo);
        }
    }
    

    可以在DbContext的OnConfiguring方法中替换EF default mapping服务:

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.ReplaceService<IRelationalTypeMappingSource, CommonComponents.Data.SqlServerTypeMappingSource>();
            optionsBuilder.UseSqlServer(Data.Configuration.ConnectionString);
        }
    

    现在在POCO中指定属性如下:

        [Column(Order = 10, TypeName = "char(8)")]
        public DateTime? SomeDate { get; set; }
    
        2
  •  -1
  •   Abdou    6 年前
    public class ClassName
    {
        private const format = "yyyyMMdd" //for example
    
        [NotMapped]
        public DateTime Date {
           get {
                 DateTime.Parse(Date, format, CultureInfo.InvariantCulture);
               }
           set {
                 Date = value.ToString(format);
               }
         }
    
         [column("Date")]
         public string StringDate { get; set; }
    }