Entity Framework - Code First - Can't Store List<String>

Entity Framework - Code First - Can't Store List String

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.

Entity Framework Code First List string Property Mapping

No, EF doesn't have any type converters or custom type mappers as alternative to model binders from MVC. You must always use some hack to force persistence. Another way to do the same is mapping TextOptions as collection of related entities. It will make your separation of concerns better but it will complicate your model and working with Variable.

public class Variable
{
public string Name { get; set; }

public int Id { get; set; }

public IList<TextOption> TextOptions
{
get;
set;
}
}

public class TextOption
{
public int Id { get; set; }
public string Text { get; set; }
}

Entity Framework options to map list of strings or list of int (List string )

Although I do not like to answer my own question, but here is what solved my problem:

After I found this link about Complex Types I 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).

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.

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.

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).

It's also important to note, that since everything is stored within one column, you cannot search for 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
}

Entity Framework 5, MVC 4, Code First - Persisting a List string in a class

As evanmcdonnal said, EF can't map a plain list of strings to a database. A simple solution would be to create a Tag entity:

public class Tag
{
public Place Place { get; set; }
public string Name { get; set; }
}

Then in your place entity, use a list of Tags:

public List<Tag> Tags { get; set; }

//just a helper, not required
public void AddTag(string tagName)
{
Tag tag = new Tag { Name = tagName };
Tags.Add(tag);
}

The result will be a new Tags table in your database that contains a foreign key back to the Places table.

EF 4.1 Code First doesn't create column for List string

I have bad news for you. EF doesn't do anything like that. If you want any serialization and deserialization you must do it yourselves = you must expose and map property with serialized value:

private IList<String> _receivers;
// This will be skipped
public IList<String> Receivers
{
get
{
return _receivers;
}
set
{
_receivers = value;
}
}

// This will be mapped
public string ReceiversSer
{
get
{
return String.Join(";", _receivers);
}
set
{
_receivers = value.Split(';').ToList();
}
}

Now ReceiversSer will be mapped to a column in the database.



Related Topics



Leave a reply



Submit