Подтвердить что ты не робот

Параметры Entity Framework для отображения списка строк или списка int (List <string>)

Я хочу сохранить объект, содержащий список примитивов с использованием EF.

public class MyObject {
    public int Id {get;set;}
    public virtual IList<int> Numbers {get;set;}
}

Я знаю, что EF не может хранить, но я хотел бы знать возможные решения для решения этой проблемы.

2 решения, о которых я могу думать, следующие:

1. Создайте объект Dummy, у которого есть Id и Integervalue, например

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. Сохранять значения списка как 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(",");
         }
    }
}

Проблема с подходом 2. заключается в том, что я должен создать реализацию Custom IList для отслеживания, если кто-то изменяет возвращенную коллекцию.

Есть ли лучшее решение для этого?

4b9b3361

Ответ 1

Хотя я не люблю отвечать на свой вопрос, но вот что решило мою проблему:

После того, как я нашел эту ссылку о Сложные типы, я попробовал несколько реализаций, и после некоторой головной боли я закончил с это.

Значения List сохраняются в виде строки в таблице напрямую, поэтому для получения записей списка не требуется выполнять несколько объединений. Разработчики должны выполнить цепочку для каждой записи списка в неизменяемой строке (см. Пример кода).

Большая часть кода обрабатывается в Baseclass (PersistableScalarCollection). Вам нужно извлечь из него только один тип данных (int, string и т.д.) И реализовать метод сериализации/десериализации значения.

Важно отметить, что вы не можете напрямую использовать базовый базовый класс (при удалении абстрактного текста). Кажется, что EF не может с этим справиться. Вы также должны обязательно аннотировать производный класс атрибутом [ComplexType].

Также обратите внимание, что кажется, что не возможно реализовать ComplexType для IList<T>, потому что EF жалуется на Indexer (поэтому я продолжал работу с ICollection).

Также важно отметить, что, поскольку все хранится в одном столбце, вы не можете искать для значений в коллекции (по крайней мере, в базе данных). В этом случае вы можете пропустить эту реализацию или денормализовать данные для поиска.

Пример для набора целых чисел:

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

Пример использования:

public class MyObject {
    public int Id {get;set;}
    public virtual PersistableIntCollection Numbers {get;set;}
}

Это базовый класс, который обрабатывает аспект персистентности, сохраняя записи списка в строке:

    /// <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
    }