用于映射字符串列表或 int 列表的实体框架选项 (List<string>)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11985267/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entity Framework options to map list of strings or list of int (List<string>)
提问by Bernhard Kircher
I want to store an object that contains a List of primitives using EF.
我想使用 EF 存储一个包含基元列表的对象。
public class MyObject {
public int Id {get;set;}
public virtual IList<int> Numbers {get;set;}
}
I know that EF cannot storethis, but I'd like to know possible solutions to solve this problem.
我知道EF 不能存储这个,但我想知道解决这个问题的可能解决方案。
The 2 Solutions I can think of are:
我能想到的2个解决方案是:
1.Create a Dummy objectthat has an Id and the Integervalue, e.g.
1.创建一个具有 Id 和 Integervalue的 Dummy 对象,例如
public class MyObject {
public int Id {get;set;}
public virtual IList<MyInt> Numbers {get;set;}
}
public class MyInt {
public int Id {get;set;}
public int Number {get;set;}
}
2.Store the list values as a blob, e.g.
2.将列表值存储为 blob,例如
public class MyObject {
public int Id {get;set;}
/// use NumbersValue to persist/load the list values
public string NumbersValue {get;set;}
[NotMapped]
public virtual IList<int> Numbers {
get {
return NumbersValue.split(',');
}
set {
NumbersValue = value.ToArray().Join(",");
}
}
}
The Problem with the 2. approach is, that I have to create a Custom IList implementation to keep track if someone modifies the returned collection.
2. 方法的问题是,如果有人修改了返回的集合,我必须创建一个自定义 IList 实现来跟踪。
Is there a better solution for this?
有没有更好的解决方案?
采纳答案by Bernhard Kircher
Although I do not like to answer my own question, but here is what solved my problem:
虽然我不喜欢回答我自己的问题,但这里是解决我的问题的方法:
After I found this link about Complex TypesI tried several implementations, and after some headache I ended up with this.
在我找到这个关于复杂类型的链接后,我尝试了几种实现,经过一些头痛,我最终得到了这个。
The List values get stored as a string on the table directly, so it's not required to perform several joins in order to get the list entries. Implementors only have to implement the conversation for each list entry to a persistable string (see the Code example).
List 值直接作为字符串存储在表中,因此不需要执行多个连接来获取列表条目。实现者只需将每个列表条目的对话实现为一个可持久化的字符串(参见代码示例)。
Most of the code is handled in the Baseclass (PersistableScalarCollection). You only have to derive from it per datatype (int, string, etc) and implement the method to serialize/deserialize the value.
大多数代码在基类(PersistableScalarCollection)中处理。您只需根据数据类型(int、string 等)从它派生并实现序列化/反序列化值的方法。
It's important to note, that you cannot use the the generic baseclass directly(when you remove the abstract). It seems that EF cannot work with that. You also have to make sure to annotate the derived class with the [ComplexType]
attribute.
需要注意的是,您不能直接使用泛型基类(当您删除抽象时)。EF 似乎无法使用它。您还必须确保使用[ComplexType]
属性注释派生类。
Also note that it seems not to be possible to implement a ComplexType for IList<T>
because EF complains about the Indexer (therefore I went on with ICollection).
另请注意,似乎不可能实现 ComplexTypeIList<T>
因为 EF 抱怨索引器(因此我继续使用 ICollection)。
It's also important to note, that since everything is stored within one column, you cannot searchfor values in the Collection (at least on the database). In this case you may skip this implementation or denormalize the data for searching.
还需要注意的是,由于所有内容都存储在一个列中,因此您无法在集合中搜索值(至少在数据库中)。在这种情况下,您可以跳过此实现或对数据进行非规范化以进行搜索。
Example for a Collection of integers:
整数集合的示例:
/// <summary>
/// ALlows persisting of a simple integer collection.
/// </summary>
[ComplexType]
public class PersistableIntCollection : PersistableScalarCollection<int> {
protected override int ConvertSingleValueToRuntime(string rawValue) {
return int.Parse(rawValue);
}
protected override string ConvertSingleValueToPersistable(int value) {
return value.ToString();
}
}
Usage example:
用法示例:
public class MyObject {
public int Id {get;set;}
public virtual PersistableIntCollection Numbers {get;set;}
}
This is the baseclass that handles the persistence aspect by storing the list entries within a string:
这是通过将列表条目存储在字符串中来处理持久性方面的基类:
/// <summary>
/// Baseclass that allows persisting of scalar values as a collection (which is not supported by EF 4.3)
/// </summary>
/// <typeparam name="T">Type of the single collection entry that should be persisted.</typeparam>
[ComplexType]
public abstract class PersistableScalarCollection<T> : ICollection<T> {
// use a character that will not occur in the collection.
// this can be overriden using the given abstract methods (e.g. for list of strings).
const string DefaultValueSeperator = "|";
readonly string[] DefaultValueSeperators = new string[] { DefaultValueSeperator };
/// <summary>
/// The internal data container for the list data.
/// </summary>
private List<T> Data { get; set; }
public PersistableScalarCollection() {
Data = new List<T>();
}
/// <summary>
/// Implementors have to convert the given value raw value to the correct runtime-type.
/// </summary>
/// <param name="rawValue">the already seperated raw value from the database</param>
/// <returns></returns>
protected abstract T ConvertSingleValueToRuntime(string rawValue);
/// <summary>
/// Implementors should convert the given runtime value to a persistable form.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract string ConvertSingleValueToPersistable(T value);
/// <summary>
/// Deriving classes can override the string that is used to seperate single values
/// </summary>
protected virtual string ValueSeperator {
get {
return DefaultValueSeperator;
}
}
/// <summary>
/// Deriving classes can override the string that is used to seperate single values
/// </summary>
protected virtual string[] ValueSeperators {
get {
return DefaultValueSeperators;
}
}
/// <summary>
/// DO NOT Modeify manually! This is only used to store/load the data.
/// </summary>
public string SerializedValue {
get {
var serializedValue = string.Join(ValueSeperator.ToString(),
Data.Select(x => ConvertSingleValueToPersistable(x))
.ToArray());
return serializedValue;
}
set {
Data.Clear();
if (string.IsNullOrEmpty(value)) {
return;
}
Data = new List<T>(value.Split(ValueSeperators, StringSplitOptions.None)
.Select(x => ConvertSingleValueToRuntime(x)));
}
}
#region ICollection<T> Members
public void Add(T item) {
Data.Add(item);
}
public void Clear() {
Data.Clear();
}
public bool Contains(T item) {
return Data.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex) {
Data.CopyTo(array, arrayIndex);
}
public int Count {
get { return Data.Count; }
}
public bool IsReadOnly {
get { return false; }
}
public bool Remove(T item) {
return Data.Remove(item);
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator() {
return Data.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
return Data.GetEnumerator();
}
#endregion
}