I wrote such class:
我写这样的类:
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"
};
}
}
and
和
internal class DataContext : DbContext
{
public DbSet<Test> Tests { get; set; }
}
After run code:
在运行代码:
var db = new DataContext();
db.Tests.Add(new Test());
db.SaveChanges();
my data is getting saved but just the Id
. I don't have any tables nor relationships applying to Strings list.
我的数据被保存,但只有Id。我没有任何表或关系应用到字符串列表。
What am I doing wrong? I tried also to make Strings virtual
but it didn't change anything.
我做错了什么?我也试着把字符串变成虚拟的,但是没有改变任何东西。
Thank you for your help.
谢谢你的帮助。
6 个解决方案
#1
102
Entity Framework does not support collections of primitive types. You can either create an entity (which will be saved to a different table) or do some string processing to save your list as a string and populate the list after the entity is materialized.
实体框架不支持原始类型的集合。您可以创建一个实体(将保存到另一个表中),也可以进行一些字符串处理,将列表保存为字符串,并在实体物化后填充列表。
#2
27
I Know this is a old question, and Pawel has given the correct answer, I just wanted to show a code example of how to do some string processing, and avoid an extra class for the list of a primitive type.
我知道这是一个老问题,Pawel已经给出了正确的答案,我只是想展示一个如何进行一些字符串处理的代码示例,并避免为原始类型的列表提供额外的类。
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(); }
}
}
#3
18
JSON.NET to the rescue.
You serialize it to JSON to persist in the Database and Deserialize it to reconstitute the .NET collection. This seems to perform better than I expected it to with Entity Framework 6 & SQLite. I know you asked for List<string>
but here's an example of an even more complex collection that works just fine.
您将它序列化为JSON以持久化到数据库中,并反序列化以重新构建. net集合。这似乎比我在实体框架6和SQLite中预期的要好。我知道您要求List
I tagged the persisted property with [Obsolete]
so it would be very obvious to me that "this is not the property you are looking for" in the normal course of coding. The "real" property is tagged with [NotMapped]
so Entity framework ignores it.
我用[废止]标记了持久化属性,因此在正常的编码过程中“这不是您要寻找的属性”对我来说是非常明显的。“真实”属性被标记为[notmapping],因此实体框架会忽略它。
(unrelated tangent): You could do the same with more complex types but you need to ask yourself did you just make querying that object's properties too hard for yourself? (yes, in my case).
(不相关的正切):您可以对更复杂的类型执行相同的操作,但是您需要问问自己,您是否只是让查询对象的属性对您自己来说太难了?(是的,在我的例子中)。
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);
}
}
#4
9
Just to simplify -
为了简化,
Entity framework doesn't support primitives. You either create a class to wrap it or add another property to format the list as a string:
实体框架不支持原语。您要么创建一个类来包装它,要么添加另一个属性来将列表格式化为字符串:
public ICollection<string> List { get; set; }
public string ListString
{
get { return string.Join(",", List); }
set { List = value.Split(',').ToList(); }
}
#5
5
Of course Pawel has given the right answer. But I found in this post that since EF 6+ it is possible to save private properties. So I would prefer this code, because you are not able to save the Strings in a wrong way.
帕维尔当然给出了正确的答案。但我在这篇文章中发现,从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
2
You can use this ScalarCollection
container that confines an array and provides some manipulation options (Gist):
您可以使用这个ScalarCollection容器来限制数组,并提供一些操作选项(Gist):
Usage:
用法:
public class Person
{
public int Id { get; set; }
//will be stored in database as single string.
public SaclarStringCollection Phones { get; set; } = new ScalarStringCollection();
}
Code:
代码:
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);
}
}
#1
102
Entity Framework does not support collections of primitive types. You can either create an entity (which will be saved to a different table) or do some string processing to save your list as a string and populate the list after the entity is materialized.
实体框架不支持原始类型的集合。您可以创建一个实体(将保存到另一个表中),也可以进行一些字符串处理,将列表保存为字符串,并在实体物化后填充列表。
#2
27
I Know this is a old question, and Pawel has given the correct answer, I just wanted to show a code example of how to do some string processing, and avoid an extra class for the list of a primitive type.
我知道这是一个老问题,Pawel已经给出了正确的答案,我只是想展示一个如何进行一些字符串处理的代码示例,并避免为原始类型的列表提供额外的类。
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(); }
}
}
#3
18
JSON.NET to the rescue.
You serialize it to JSON to persist in the Database and Deserialize it to reconstitute the .NET collection. This seems to perform better than I expected it to with Entity Framework 6 & SQLite. I know you asked for List<string>
but here's an example of an even more complex collection that works just fine.
您将它序列化为JSON以持久化到数据库中,并反序列化以重新构建. net集合。这似乎比我在实体框架6和SQLite中预期的要好。我知道您要求List
I tagged the persisted property with [Obsolete]
so it would be very obvious to me that "this is not the property you are looking for" in the normal course of coding. The "real" property is tagged with [NotMapped]
so Entity framework ignores it.
我用[废止]标记了持久化属性,因此在正常的编码过程中“这不是您要寻找的属性”对我来说是非常明显的。“真实”属性被标记为[notmapping],因此实体框架会忽略它。
(unrelated tangent): You could do the same with more complex types but you need to ask yourself did you just make querying that object's properties too hard for yourself? (yes, in my case).
(不相关的正切):您可以对更复杂的类型执行相同的操作,但是您需要问问自己,您是否只是让查询对象的属性对您自己来说太难了?(是的,在我的例子中)。
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);
}
}
#4
9
Just to simplify -
为了简化,
Entity framework doesn't support primitives. You either create a class to wrap it or add another property to format the list as a string:
实体框架不支持原语。您要么创建一个类来包装它,要么添加另一个属性来将列表格式化为字符串:
public ICollection<string> List { get; set; }
public string ListString
{
get { return string.Join(",", List); }
set { List = value.Split(',').ToList(); }
}
#5
5
Of course Pawel has given the right answer. But I found in this post that since EF 6+ it is possible to save private properties. So I would prefer this code, because you are not able to save the Strings in a wrong way.
帕维尔当然给出了正确的答案。但我在这篇文章中发现,从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
2
You can use this ScalarCollection
container that confines an array and provides some manipulation options (Gist):
您可以使用这个ScalarCollection容器来限制数组,并提供一些操作选项(Gist):
Usage:
用法:
public class Person
{
public int Id { get; set; }
//will be stored in database as single string.
public SaclarStringCollection Phones { get; set; } = new ScalarStringCollection();
}
Code:
代码:
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);
}
}