私はそのようなクラスを書きました:
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"
};
}
}
そして
internal class DataContext : DbContext
{
public DbSet<Test> Tests { get; set; }
}
コードを実行した後:
var db = new DataContext();
db.Tests.Add(new Test());
db.SaveChanges();
データは保存されますが、Id
のみが保存されます。 Stringsリストに適用されるテーブルもリレーションシップもありません。
私は何を間違えていますか? Stringsvirtual
を作成しようとしましたが、何も変わりませんでした。
ご協力ありがとうございました。
Entity Frameworkは、プリミティブ型のコレクションをサポートしていません。エンティティ(別のテーブルに保存される)を作成するか、何らかの文字列処理を行ってリストを文字列として保存し、エンティティが実体化された後にリストに入力することができます。
これは古い質問であり、 Pawelが正しい答えを与えている であることを知っていますタイプ。
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+:
プロパティ:
public string[] Strings { get; set; }
OnModelCreating:
modelBuilder.Entity<YourEntity>()
.Property(e => e.Strings)
.HasConversion(
v => string.Join(',', v),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));
データベースに永続化するにはJSONにシリアル化し、.NETコレクションを再構成するにはシリアル化を解除します。これは、Entity Framework 6およびSQLiteを使用した場合に期待したよりもパフォーマンスが高いようです。あなたがList<string>
を要求したことは知っていますが、ここではうまく機能するさらに複雑なコレクションの例を示します。
永続プロパティに[Obsolete]
のタグを付けたので、通常のコーディングの過程で「これはあなたが探しているプロパティではない」ことは非常に明白です。 「実際の」プロパティには[NotMapped]
のタグが付けられているため、Entity Frameworkはそれを無視します。
(無関係な接線):より複雑な型でも同じことができますが、自分自身にそのオブジェクトのプロパティを照会するのが非常に難しいのか自問する必要がありますか? (はい、私の場合)。
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);
}
}
単純化するために-
エンティティフレームワークはプリミティブをサポートしていません。クラスを作成してラップするか、別のプロパティを追加してリストを文字列としてフォーマットします。
public ICollection<string> List { get; set; }
public string ListString
{
get { return string.Join(",", List); }
set { List = value.Split(',').ToList(); }
}
この答えは、 @ Sasan および @ CAD bloke によって提供されるものに基づいています。
JsonConvert
)builder.Entity<YourEntity>().Property(p => p.Strings)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<List<string>>(v));
EF Core fluent構成を使用して、List
をJSONとの間でシリアライズ/デシリアライズします。
このコードがあなたが努力できるすべてのものの完璧な組み合わせである理由:
もちろん Pawelが正しい答えを出した 。しかし、私はこれを見つけました post EF 6+以降、プライベートプロパティを保存することが可能です。あなたは間違った方法で文字列を保存することができないので、私はこのコードを好むでしょう。
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"
};
}
}
配列を制限し、いくつかの操作オプションを提供するこのScalarCollection
コンテナを使用できます( Gist ):
使用法:
public class Person
{
public int Id { get; set; }
//will be stored in database as single string.
public SaclarStringCollection Phones { get; set; } = new ScalarStringCollection();
}
コード:
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);
}
}