DB に配列を格納することの是非はさておき、EntityFramework Core を使って SQLite に配列を格納する方法です。
環境
- Visual Studio 2017
- .NET Core 2.2
モデル
イメージとして、以下のような int
の配列を持つオブジェクトを DB に格納したいとします。
public class PersonDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=sqlitetest.db");
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int[] Attributes { get; set; }
}
しかし、このままビルドすると以下のようなエラーが表示されます。
System.InvalidOperationException: 'The property 'Person.Attributes' could not be mapped, because it is of type 'Int32[]' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.'
配列を保存するためには
DB には配列を文字列として格納し、取り出すときに分割して int
に変換するようにします。
先ほどのコードを以下のように変更します。
public class PersonDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=sqlitetest.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Property<string>("AttributeCollection")
.HasField("_attributes");
}
}
public class Person
{
private static readonly char delimiter = ';';
public int Id { get; set; }
public string Name { get; set; }
string _attributes;
[NotMapped]
public int[] Attributes
{
get => _attributes.Split(delimiter).Select(x => int.Parse(x)).ToArray();
set => _attributes = string.Join(delimiter, value);
}
}
int[] Attributes
には NotMapped
属性を付与することで、O/R マッパーの対象から外します。
その代わりに string _attributes
というフィールドを用意し、int[] Attributes
のプロパティで、_attributes
に対する get
と set
を用意します。int
配列と文字列の変換には、デリミターとして ;
を使用しています。
DbContext
クラスの OnModelCreating
メソッドでは、_attributes
フィールドに AttributeCollection
というプロパティを割り当てるように設定しています。
以上で、Person
クラスを使う側からは DB を意識せずに配列を扱うことができます。
using (var db = new PersonDbContext())
{
var person = new Person
{
Name = name,
Attributes = new int[] { 0, 1, 2 }
};
db.Persons.Add(person);
db.SaveChanges();
}
using (var db = new PersonDbContext())
{
foreach (var person in db.Persons)
{
Console.WriteLine($"ID = {person.Id}, Name = {person.Name}, Attributes={string.Join(", ", person.Attributes)}");
}
}
DB Browser for SQLite で中身を見ると、文字列として格納されているのがわかります。