如何用实体框架代码优先的方法存储双[]数组到数据库

本文关键字:存储 数组 数据库 方法 实体 何用 框架 代码 | 更新日期: 2023-09-27 18:02:23

如何使用实体框架代码优先存储双精度数组到数据库,而不影响现有代码和架构设计?

我看过数据注释和Fluent API,我也考虑过将双数组转换为字节串,并将该字节存储到数据库中自己的列中。

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

类型double[]必须是一个非空值类型才能使用它作为参数'T'。

存储Data的类已成功存储到数据库中,以及与该类的关系。我只漏掉了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帖子:字符串为双精度数组和

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

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; }
}

这将映射一个单一的实体(这里是'Reference')到一个'list'的原始类。这基本上是为了让SQL数据库高兴,并允许您适当地使用您的数据列表。

这可能不符合你的需要,但将是一个让EF高兴的方法。

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

在我看来,几乎所有其他的答案都是相反的。实体EF应该管理字符串,数组必须从它生成。因此,只有当字符串被EF访问时,才必须对整个数组进行读写。

涉及Data[]逻辑的解决方案是错误的,因为正如我在评论中所写的那样,您将遇到矛盾的条件。在所有其他条件下,变量必须保持为纯数组。通过添加"get"answers";set"在Data[]中的逻辑,正如我目前所看到的,这会发生:

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

2 -当你去设置单个元素时,不会被存储,因为它会经过"get"而不是"set"。如果你试图声明Data=new []{0,0,0},然后设置Data[1]=2,要重新读取数据[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所独有的。

一旦从DB中读取字符串,它就与Data_string相关联,通过set创建Data数组。此时,您可以处理Data,而不会以任何方式影响字符串,就像普通数组一样。当您要求EF在DB中保存时,通过Data_string属性中的get,字符串将基于Data元素完全重构,然后作为字符串存储。

实际上,字符串只被修改了两次,在从DB读取的时候和保存的时候。

在我看来,这个解决方案比在字符串上连续操作要有效得多。

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。解析它将导致FormatException,这是你真的不想要的!

  2. 它比较慢,因为您的服务器必须解析字符串。为什么解析字符串,而不是得到几乎准备好的数据从SQL服务器使用?

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

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

    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适用于整个数组,但如果您只需要从数组中获取一个元素,则可以创建一个从数组中获取单个元素的函数,复杂度为0 (1):

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

for Set:

    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];
    }

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

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

另外,在string属性上指定[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的智能感知隐藏公共方法