实体框架 - 代码优先 - 无法存储列表<string> </string>

时间:2013-12-20 21:08:53

标签: c# .net entity-framework

我写了这样的课:

class Test
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public List<String> Strings { get; set; }

    public Test()
    {
        Strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }
}

internal class DataContext : DbContext
{
    public DbSet<Test> Tests { get; set; }
}

运行代码后:

var db = new DataContext();
db.Tests.Add(new Test());
db.SaveChanges();

我的数据已保存,但仅保存Id。我没有适用于字符串列表的任何表格和关系。

我做错了什么?我还尝试制作字符串 virtual,但它没有改变任何内容。

感谢您的帮助。

10 个答案:

答案 0 :(得分:135)

实体框架不支持基本类型的集合。您可以创建一个实体(将保存到另一个表)或执行一些字符串处理以将列表保存为字符串,并在实体实现后填充列表。

答案 1 :(得分:34)

我知道这是一个老问题,Pawel has given the correct answer,我只想展示一个如何进行字符串处理的代码示例,并避免为基本类型列表添加额外的类。

public class Test
{
    public Test()
    {
        _strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    private List<String> _strings { get; set; }

    public List<string> Strings
    {
        get { return _strings; }
        set { _strings = value; }
    }

    [Required]
    public string StringsAsString
    {
        get { return String.Join(',', _strings); }
        set { _strings = value.Split(',').ToList(); }
    }
}

答案 2 :(得分:27)

EF Core 2.1 +:

属性:

public string[] Strings { get; set; }

OnModelCreating:

modelBuilder.Entity<YourEntity>()
            .Property(e => e.Strings)
            .HasConversion(
                v => string.Join(',', v),
                v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));

答案 3 :(得分:21)

JSON.NET救援。

将序列化为JSON以保留在数据库中并对其进行反序列化以重构.NET集合。这似乎比我预期的Entity Framework 6&amp; SQLite的。我知道你要求Promise.resolve,但这里有一个更复杂的收藏品的例子。

我用List<string>标记了持久性属性,因此对我来说非常明显&#34;这不是您正在寻找的属性&#34;在正常的编码过程中。 &#34;真实&#34;属性标记为[Obsolete],因此实体框架忽略它。

(无关切线):你可以对更复杂的类型做同样的事情,但是你需要问问自己,你是否只是自己查询对象的属性太难了? (是的,就我而言)。

[NotMapped]

答案 4 :(得分:10)

只是为了简化 -

实体框架不支持基元。您可以创建一个类来包装它,也可以添加另一个属性来将列表格式化为字符串:

public ICollection<string> List { get; set; }
public string ListString
{
    get { return string.Join(",", List); }
    set { List = value.Split(',').ToList(); }
}

答案 5 :(得分:6)

当然Pawel has given the right answer。 但是我在这个post中发现,自EF 6+以来,可以保存私有属性。所以我更喜欢这段代码,因为你无法以错误的方式保存字符串。

public class Test
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Column]
    [Required]
    private String StringsAsStrings { get; set; }

    public List<String> Strings
    {
        get { return StringsAsStrings.Split(',').ToList(); }
        set
        {
            StringsAsStrings = String.Join(",", value);
        }
    }
    public Test()
    {
        Strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }
}

答案 6 :(得分:4)

稍微调整https://github.com/darwinex/DarwinexLabs/tree/master/tools/dwx_zeromq_connector/v2.0.1#installation@Mathieu Viales,这是一个使用新的System.Text.Json序列化程序的.NET Standard兼容代码段,从而消除了对Newtonsoft.Json的依赖。

<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" /> to <PackageReference Include="Microsoft.AspNetCore.App" />

请注意,尽管using System.Text.Json; builder.Entity<YourEntity>().Property(p => p.Strings) .HasConversion( v => JsonSerializer.Serialize(v, default), v => JsonSerializer.Deserialize<List<string>>(v, default)); Serialize()中的第二个参数通常是可选的,但会出现错误:

表达式树可能不包含使用以下内容的调用或调用 可选参数

分别将其明确设置为默认值(null)即可清除

答案 7 :(得分:2)

您可以使用限制数组的ScalarCollection容器并提供一些操作选项(Gist):

用法:

public class Person
{
    public int Id { get; set; }
    //will be stored in database as single string.
    public SaclarStringCollection Phones { get; set; } = new ScalarStringCollection();
}

代码:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace System.Collections.Specialized
{
#if NET462
  [ComplexType]
#endif
  public abstract class ScalarCollectionBase<T> :
#if NET462
    Collection<T>,
#else
    ObservableCollection<T>
#endif
  {
    public virtual string Separator { get; } = "\n";
    public virtual string ReplacementChar { get; } = " ";
    public ScalarCollectionBase(params T[] values)
    {
      if (values != null)
        foreach (var item in Items)
          Items.Add(item);
    }

#if NET462
    [Browsable(false)]
#endif
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("Not to be used directly by user, use Items property instead.")]
    public string Data
    {
      get
      {
        var data = Items.Select(item => Serialize(item)
          .Replace(Separator, ReplacementChar.ToString()));
        return string.Join(Separator, data.Where(s => s?.Length > 0));
      }
      set
      {
        Items.Clear();
        if (string.IsNullOrWhiteSpace(value))
          return;

        foreach (var item in value
            .Split(new[] { Separator }, 
              StringSplitOptions.RemoveEmptyEntries).Select(item => Deserialize(item)))
          Items.Add(item);
      }
    }

    public void AddRange(params T[] items)
    {
      if (items != null)
        foreach (var item in items)
          Add(item);
    }

    protected abstract string Serialize(T item);
    protected abstract T Deserialize(string item);
  }

  public class ScalarStringCollection : ScalarCollectionBase<string>
  {
    protected override string Deserialize(string item) => item;
    protected override string Serialize(string item) => item;
  }

  public class ScalarCollection<T> : ScalarCollectionBase<T>
    where T : IConvertible
  {
    protected override T Deserialize(string item) =>
      (T)Convert.ChangeType(item, typeof(T));
    protected override string Serialize(T item) => Convert.ToString(item);
  }
}

答案 8 :(得分:2)

我想补充一点,当使用Npgsql(PostgreSQL的数据提供程序)时,实际上支持原始类型的数组和列表:

https://www.npgsql.org/efcore/mapping/array.html

答案 9 :(得分:1)

此答案基于@Sasan@CAD bloke提供的答案。

仅适用于EF Core 2.1+(不兼容.NET Standard)(Newtonsoft JsonConvert

builder.Entity<YourEntity>().Property(p => p.Strings)
    .HasConversion(
        v => JsonConvert.SerializeObject(v),
        v => JsonConvert.DeserializeObject<List<string>>(v));

使用EF Core流畅配置,我们将List序列化到JSON或从JSON反序列化。

为什么此代码是您可以争取的所有东西的完美组合:

  • Sasn最初答案的问题是,如果列表中的字符串包含逗号(或选择作为分隔符的任何字符),它将变成一个大混乱,因为它将单个条目转换为多个条目。最简单,最简洁的阅读方式。
  • CAD bloke答案的问题在于它丑陋且需要更改模型,这是一种不良的设计实践(请参阅Marcell Toth对Sasan's answer的评论)。但这是唯一可以保证数据安全的答案。