用于映射字符串列表或int列表的实体框架选项(List <string>)

Ber*_*her 37 mapping orm entity-framework list

我想使用EF存储包含基元列表的对象.

public class MyObject {
    public int Id {get;set;}
    public virtual IList<int> Numbers {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

我知道EF无法存储,但我想知道解决这个问题的可能解决方案.

我能想到的2个解决方案是:

1. 创建一个具有Id和整数值的虚拟对象,例如

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;}
}
Run Code Online (Sandbox Code Playgroud)

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(",");
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

2.方法的问题是,我必须创建一个自定义IList实现来跟踪是否有人修改了返回的集合.

有更好的解决方案吗?

Ber*_*her 37

虽然我不想回答我自己的问题,但这里有什么解决了我的问题:

在我找到关于复杂类型的链接之后,我尝试了几个实现,经过一些头痛我最终得到了这个.

List值直接作为字符串存储在表中,因此不需要执行多个连接以获取列表条目.实现者只需要将每个列表条目的对话实现为可持久化的字符串(请参阅代码示例).

大多数代码都在Baseclass(PersistableScalarCollection)中处理.您只需从每个数据类型(int,string等)派生它,并实现该方法以序列化/反序列化该值.

需要注意的是,您不能直接使用通用基类(当您删除摘要时).似乎EF无法解决这个问题.您还必须确保使用该[ComplexType]属性注释派生类.

另请注意,似乎无法实现ComplexType,IList<T>因为EF抱怨索引器(因此我继续使用ICollection).

还需要注意的是,由于所有内容都存储在一列中,因此无法在Collection中搜索值(至少在数据库中).在这种情况下,您可以跳过此实现或对数据进行非规范化以进行搜索.

整数集合的示例:

    /// <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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法示例:

public class MyObject {
    public int Id {get;set;}
    public virtual PersistableIntCollection Numbers {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

这是通过在列表中存储列表条目来处理持久性方面的基类:

    /// <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
    }
Run Code Online (Sandbox Code Playgroud)


Joh*_*Gov 12

我正在使用 EF Core 并遇到了类似的问题,但以更简单的方式解决了它。

这个想法是将整数列表作为逗号分隔的字符串存储在数据库中。ValueConverter我通过在实体类型构建器中指定 a 来做到这一点。

public class MyObjectBuilder : IEntityTypeConfiguration<MyObject>
{
    public void Configure(EntityTypeBuilder<MyObject> builder)
    {
        var intArrayValueConverter = new ValueConverter<int[], string>(
            i => string.Join(",", i),
            s => string.IsNullOrWhiteSpace(s) ? new int[0] : s.Split(new[] { ',' }).Select(v => int.Parse(v)).ToArray());

        builder.Property(x => x.Numbers).HasConversion(intArrayValueConverter);
    }
}
Run Code Online (Sandbox Code Playgroud)

更多信息可以在这里找到:https://entityframeworkcore.com/knowledge-base/37370476/how-to-persist-a-list-of-strings-with-entity-framework-core-