entity framework – 代码优先 – 不能存储列表<String>

我写了这样的课:

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 。 我没有任何表格或关系适用于string列表。

我究竟做错了什么? 我也试图让string virtual但没有任何改变。

感谢您的帮助。

entity framework不支持基元types的集合。 你可以创build一个实体(将被保存到一个不同的表),或者做一些string处理来将你的列表保存为一个string,并在实体化后填充列表。

我知道这是一个古老的问题, 帕维尔给出了正确的答案 ,我只是想展示一个如何做一些string处理的代码示例,并避免了一个基本types列表的额外类。

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

JSON.NET来救援。

您将其序列化为JSON以保存在数据库中,并对其进行反序列化以重新构build.NET集合。 这似乎比我预期的Entity Framework 6和SQLite更好。 我知道你问了List<string>但这里是一个更复杂的集合的例子,工作得很好。

我用[Obsolete]标记了持久性属性,所以在正常的编码过程中,对于我来说“这不是你正在寻找的属性”是非常明显的。 “真实”属性标有[NotMapped]所以entity framework忽略它。

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

 using Newtonsoft.Json; .... [NotMapped] public Dictionary<string, string> MetaData { get; set; } = new Dictionary<string, string>(); /// <summary> <see cref="MetaData"/> for database persistence. </summary> [Obsolete("Only for Persistence by EntityFramework")] public string MetaDataJsonForDb { get { return MetaData == null || !MetaData.Any() ? null : JsonConvert.SerializeObject(MetaData); } set { if (string.IsNullOrWhiteSpace(value)) MetaData.Clear(); else MetaData = JsonConvert.DeserializeObject<Dictionary<string, string>>(value); } } 

只是为了简化 –

entity framework不支持基元。 您可以创build一个类来包装它,或者添加另一个属性来将列表格式化为一个string:

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

当然, 帕维尔给出了正确的答案 。 但是我在这篇文章中发现,既然EF 6+可以保存私有属性。 所以我更喜欢这个代码,因为你不能以错误的方式保存string。

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

您可以使用这个限制数组的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); } }