使用自定义类型处理货币

本文关键字:处理 货币 类型 自定义 | 更新日期: 2023-09-27 17:50:35

我一直在使用我的poco的自定义类型的钱,并试图将其插入到我的数据库,但它是隐含的被实体框架抛弃。

这是我的代码简化;

我的类型;

public struct Money
{
    private static decimal _value;
    public Money(Decimal value)
    {
        _value = value;
    }
    public static implicit operator Money(Decimal value)
    {
        return new Money(value);
    }
    public static implicit operator decimal(Money value)
    {
        return _value;
    }
}

我的对象;

public class MyObject
{
    [Key]
    public int Id { get; set; }
    public Money MyMoney { get; set; }
}

我的背景;

public class Data : DbContext
{
    public Data()
        : base("Data Source=.;Database=MyTest;Integrated Security=True")
    {}
    public DbSet<MyObject> MyObject { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyObject>()
            .Property(p => p.MyMoney).HasColumnName("MyMoney");
    }
}

当我使用这段代码时,我得到以下错误:

属性'MyMoney'不是类型'MyObject'上声明的属性。验证该属性是否未被明确地从模型中排除使用Ignore方法或NotMappedAttribute数据注释。确保它是一个有效的原语财产。

我想问题出在最后一句....那么,什么是有效的基本属性呢?还有其他办法解决这个问题吗?

使用自定义类型处理货币

您可以通过显式映射十进制属性并仅向调用者公开Money属性来使用它:

public class MyObject
{
    [Key]
    public int Id { get; set; }
    protected decimal MyMoney { get; set; }
    public Money MyMoneyStruct 
    { 
        get { return (Money)this.MyMoney; } 
        set { this.MyMoney = (decimal)value; }
    }
}

好的,这就是我的解决方案。

public class MyObject
{
    [Key]
    public int Id { get; set; }
    public Money MyMoney { get { return (Money)MyMoneyInternal; } set { MyMoneyInternal = (decimal)value; } }
    private decimal MyMoneyInternal { get; set; }
}
为了能够读取私有属性,我创建了一个属性扩展,如下所示。因为我的一些Money类型的属性是Nullable,所以我也必须处理它。
public static class PropertyExtensions
{
    public static PrimitivePropertyConfiguration Property<TClass, TProperty>(this EntityTypeConfiguration<TClass> etc, string propertyName)
        where TClass : class
        where TProperty : struct
    {
        PrimitivePropertyConfiguration returnValue;
        Type type = typeof(TClass);
        var propertyInfo = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        ParameterExpression parameterExpression = Expression.Parameter(type, "xyz");
        MemberExpression memberExpression = Expression.Property((Expression)parameterExpression, propertyInfo);
        if (IsNullable(memberExpression.Type))
        {
            returnValue = etc.Property((Expression<Func<TClass, TProperty?>>)Expression.Lambda(memberExpression, parameterExpression));
        }
        else
        {
            returnValue = etc.Property((Expression<Func<TClass, TProperty>>)Expression.Lambda(memberExpression, parameterExpression));
        }
        return returnValue;
    }
    private static bool IsNullable(Type type)
    {
        bool result;
        if (type.IsGenericType)
        {
            var genericType = type.GetGenericTypeDefinition();
            result = genericType.Equals(typeof(Nullable<>));
        }
        else
        {
            result = false;
        }
        return result;
    }
}

然后我可以用它来读取我的私有属性

modelBuilder.Entity<MyObject>()
            .Property<MyObject, decimal>("MyMoneyInternal").HasColumnName("MyMoney");

谢谢Steve给我指明了正确的方向。

你也可以在Complex类中转换你的"Money";这将使暴露的属性内联到数据库中。如果只使用Decimal属性,它看起来就像Money_Value(假设您将Value公开为属性)。假设您将"Currency"作为字符串添加到类中。然后,数据库中也会有Money_Currency。

要向EF声明你的类是一个复杂类型,只需用[ComplexType()]注释它。

为了获得Struct + ComplexType的最佳效果,您甚至可以同时使用两者:复杂类型在内部使用结构(ComplexType只是一个包装器)。