J'ai écrit un tel cours:
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"
};
}
}
et
internal class DataContext : DbContext
{
public DbSet<Test> Tests { get; set; }
}
Après le code d'exécution:
var db = new DataContext();
db.Tests.Add(new Test());
db.SaveChanges();
mes données sont en train d'être sauvegardées mais juste le Id
. Je n'ai aucune table ni relation s'appliquant à la liste Chaînes.
Qu'est-ce que je fais mal? J'ai aussi essayé de faire Chaînesvirtual
mais cela n'a rien changé.
Merci de votre aide.
Entity Framework ne prend pas en charge les collections de types primitifs. Vous pouvez créer une entité (qui sera enregistrée dans une table différente) ou effectuer un traitement de chaîne pour enregistrer votre liste en tant que chaîne et la remplir une fois que l'entité est matérialisée.
Je sais que c'est une vieille question et Pawel a donné la bonne réponse , je voulais juste montrer un exemple de code montrant comment effectuer un traitement de chaîne et éviter une classe supplémentaire pour la liste d'une primitive type.
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(); }
}
}
EF Core 2.1+:
Propriété:
public string[] Strings { get; set; }
OnModelCreating:
modelBuilder.Entity<YourEntity>()
.Property(e => e.Strings)
.HasConversion(
v => string.Join(',', v),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));
Vous le sérienez en JSON pour qu'il persiste dans la base de données et vous le désérialisez pour reconstituer la collection .NET. Cela semble mieux fonctionner que prévu avec Entity Framework 6 et SQLite. Je sais que vous avez demandé List<string>
, Mais voici un exemple d'une collection encore plus complexe qui fonctionne parfaitement.
J'ai étiqueté la propriété persistante avec [Obsolete]
, Il serait donc très évident pour moi que "ce n'est pas la propriété que vous recherchez" dans le cours normal du codage. La propriété "real" est étiquetée avec [NotMapped]
, Donc le cadre Entity l'ignore.
(tangente indépendante): Vous pouvez faire la même chose avec des types plus complexes, mais vous devez vous demander si vous avez juste rendu la recherche des propriétés de cet objet trop difficile pour vous-même. (oui, dans mon cas).
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);
}
}
Juste pour simplifier -
Entity Framework ne supporte pas les primitives. Vous pouvez créer une classe pour l'envelopper ou ajouter une autre propriété pour formater la liste en tant que chaîne:
public ICollection<string> List { get; set; }
public string ListString
{
get { return string.Join(",", List); }
set { List = value.Split(',').ToList(); }
}
Cette réponse est basée sur celles fournies par @ Sasan et @ CAD CAD .
JsonConvert
)builder.Entity<YourEntity>().Property(p => p.Strings)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<List<string>>(v));
En utilisant la configuration fluide d’EF Core, nous sérialisons/désérialisons le List
vers/depuis JSON.
Pourquoi ce code est le mélange parfait de tout ce que vous pourriez rechercher:
Bien sûr, Pawel a donné la bonne réponse . Mais j’ai trouvé dans ce post que, depuis EF 6+, il est possible de sauvegarder des propriétés privées. Je préférerais donc ce code, car vous ne pouvez pas enregistrer les chaînes de manière erronée.
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"
};
}
}
Vous pouvez utiliser ce conteneur ScalarCollection
qui confine un tableau et fournit des options de manipulation ( 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);
}
}