繁体   English   中英

如何使用 Entity Framework Code-First 方法将 double[] 数组存储到数据库

[英]How to store double[] array to database with Entity Framework Code-First approach

如何在不影响现有代码和体系结构设计的情况下使用 Entity Framework Code-First 将双精度数组存储到数据库?

我查看了 Data Annotation 和 Fluent API,我还考虑过将双精度数组转换为字节字符串并将该字节存储到数据库中它自己的列中。

我无法访问public double[] Data { get; set; } public double[] Data { get; set; } public double[] Data { get; set; }属性流利 API,然后我得到的错误消息是:

double[]类型必须是不可为 null 的值类型才能将其用作参数“T”。

存储Data的 class 已成功存储到数据库中,以及与此 class 的关系。我只缺少Data列。

你可以做这样的事情:

    [NotMapped]
    public double[] Data
    {
        get
        {
            string[] tab = this.InternalData.Split(',');
            return new double[] { double.Parse(tab[0]), double.Parse(tab[1]) };
        }
        set
        {
            this.InternalData = string.Format("{0},{1}", value[0], value[1]);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    public string InternalData { get; set; }

感谢大家的投入,由于您的帮助,我能够找到解决此问题的最佳方法。 这是:

 public string InternalData { get; set; }
 public double[] Data
 {
    get
    {
        return Array.ConvertAll(InternalData.Split(';'), Double.Parse);                
    }
    set
    {
        _data = value;
        InternalData = String.Join(";", _data.Select(p => p.ToString()).ToArray());
    }
 }

感谢这些 stackoverflow 帖子: String to Doubles arrayArray of Doubles to a String

我知道它有点贵,但你可以这样做

class Primitive
{
    public int PrimitiveId { get; set; }
    public double Data { get; set; }

    [Required]
    public Reference ReferenceClass { get; set; }
}

// This is the class that requires an array of doubles
class Reference
{
    // Other EF stuff

    // EF-acceptable reference to an 'array' of doubles
    public virtual List<Primitive> Data { get; set; }
}

现在,这会将单个实体(此处为“参考”)映射到原始类的“列表”。 这基本上是为了让 SQL 数据库满意,并允许您适当地使用您的数据列表。

这可能不适合您的需求,但将是让 EF 满意的一种方式。

如果您使用List<double>而不是double[]会容易得多。 您已经有一个表来存储您的Data值。 您可能有从某个表到存储双精度值的表的外键。 创建另一个模型来反映存储双精度的表,并在映射类中添加外键映射。 这样你就不需要添加一些复杂的后台逻辑来检索或存储类属性中的值。

Nathan White 给出了最好的答案(得到了我的投票)。

这是对 Joffrey Kern 的回答的一个小改进,以允许任何长度的列表(未经测试):

    [NotMapped]
    public IEnumerable<double> Data
    {
        get
        {
            var tab = InternalData.Split(',');
            return tab.Select(double.Parse).AsEnumerable();
        }
        set { InternalData = string.Join(",", value); }
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    public string InternalData { get; set; }

不要使用 double[] 使用 List 插入。

像这样。

public class MyModel{
    ...
    public List<MyClass> Data { get; set; }
    ...
}

public class MyClass{
    public int Id { get; set; }
    public double Value { get; set; }
}

我看到的所有解决方案都很糟糕,因为:

  1. 如果你创建表,你不想存储这样的数据:“99.5,89.65,78.5,15.5”这是无效的! 首先它是一个字符串,这意味着如果您可以在其中键入字母,并且当您的 ASP.NET 服务器调用 double.Parse 时,它​​将导致 FormatException 并且您真的不想要!

  2. 它更慢,因为您的服务器必须解析字符串。 为什么要解析字符串而不是从 SQL Server 获取几乎准备好的数据来使用?

如果您的集合可以为null ,并且您希望保留它,请执行以下操作:

[NotMapped]
public double[] Data
{
    get => InternalData != null ? Array.ConvertAll(Data.Split(';'), double.Parse) : null;
    set => InternalData = value != null ? string.Join(";", value) : null;
}

此外,在字符串属性上指定[Column(TypeName = "varchar")]以获得更有效的存储数据类型。

@Jonas答案的完美改进是添加必要的注释。 所以,一个更干净的版本是

[EditorBrowsable(EditorBrowsableState.Never)]
[JsonIgnore]
public string InternalData { get; set; }

[NotMapped]
public double[] Data
{
    get => Array.ConvertAll(InternalData.Split(';'), double.Parse);
    set 
    {
        InternalData = string.Join(";", value.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray());
    }
}

[JsonIgnore]注释将忽略来自 JSON 序列化和 Swagger UI 的 InternalData 字段。

[EditorBrowsable(EditorBrowsableState.Never)]将从 IDE IntelliSense 隐藏公共方法

在我看来,几乎所有其他答案都与应有的相反。 实体 EF 应该管理字符串,并且必须从中生成数组。 因此,只有当 EF 访问字符串时,才必须完整读取和写入数组。

涉及 Data[] 逻辑的解决方案是错误的,因为正如我在评论中所写,您会遇到矛盾的情况。 在所有其他条件下,变量必须保持为纯数组。 通过将“get”和“set”逻辑放在 Data[] 中,正如我目前所见,会发生这种情况:

1 - 每次对数组进行索引访问时,都会从字符串自动重新创建数组。 这是一个无用的工作,想想一个循环中的迭代......

2 - 当您设置单个元素时,它不会被存储,因为它通过“get”而不是“set”。 如果您尝试声明Data=new []{0,0,0}然后设置Data[1]=2 ,则重新读取 Data[1] 结果为 0。

我的解决方案是完全扭转逻辑。

    public string Data_string
    {
        get => string.Join(';', Data??Array.Empty());
        set => Data= value == null ? Array.Empty<double>() : Array.ConvertAll(value.Split(';',StringSplitOptions.RemoveEmptyEntries), double.Parse);
    }
   
    [NotMapped]
    public double[] Data {get;set;}
   

显然这仅适用于在数据库上存储和检索数据,对 Data_string 的访问是 EF 独有的。

我知道这篇文章很古老,但如果有人仍然需要做这样的事情,请不要使用上述解决方案,

由于上述解决方案效率极低(性能和磁盘空间明智)..,最好的方法是将数组存储为字节数组

    public byte[] ArrayData;
    
    [NotMapped]
    public double[] Array {
        get {
            var OutputArray = new double[ArrayData.Length / 8];

            for (int i = 0;i < ArrayData.Length / 8;i++)
                OutputArray[i] = BitConverter.ToDouble(ArrayData, i * 8);

            return OutputArray;
        }

        set {
            var OutputData = new byte[value.Length * 8];

            for (int i = 0;i < value.Length;i++) {
                var BinaryValue = BitConverter.GetBytes(value[i]);

                OutputData[(i*8)] = BinaryValue[0];
                OutputData[(i*8)+1] = BinaryValue[1];
                OutputData[(i*8)+2] = BinaryValue[2];
                OutputData[(i*8)+3] = BinaryValue[3];
                           
                OutputData[(i*8)+4] = BinaryValue[4];
                OutputData[(i*8)+5] = BinaryValue[5];
                OutputData[(i*8)+6] = BinaryValue[6];
                OutputData[(i*8)+7] = BinaryValue[7];
            }

            ArrayData = OutputData;
        }
    }

`

如果您需要更高的性能,您可以使用不安全代码并使用指针……而不是 BitConverter……

这比将双精度值(可能会变得很大)保存为字符串,然后拆分字符串数组要好得多! 然后将字符串解析为 double !!!

这些 getter/setter 作用于整个数组,但如果您只需要从数组中获取一个项目,您可以创建一个函数从数组中获取单个项目,复杂度为 O(1):

忘记:

    public double Array_GetValue(int Index) {
        return BitConverter.ToDouble(ArrayData, Index * 8);
    }

对于集合:

    public void Array_SetValue(int Index, double Value) {
        var BinaryValue = BitConverter.GetBytes(Value);

        ArrayData[(Index*8)] = BinaryValue[0];
        ArrayData[(Index*8)+1] = BinaryValue[1];
        ArrayData[(Index*8)+2] = BinaryValue[2];
        ArrayData[(Index*8)+3] = BinaryValue[3];

        ArrayData[(Index*8)+4] = BinaryValue[4];
        ArrayData[(Index*8)+5] = BinaryValue[5];
        ArrayData[(Index*8)+6] = BinaryValue[6];
        ArrayData[(Index*8)+7] = BinaryValue[7];
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM