EntityFramework Core　自分用メモ

デスクトップにテキストでおいてあったけどなくしそうだったのでメモ。
※参考にしないでください

#####⓪DBマイグレ
* 初回
Enable-Migrations

* アップデート用ファイル作成
Add-Migration 投稿名

* 実行
Update-Database -Verbose


####Entity Framework Core アノテーション

#####①[NotMapped]：DBに追加しないModelのプロパティ

#####②主キー
　単体の場合：[Key]
　　　modelBuilder.Entity<モデル名>().HasKey(c => c.キー名);
　複合キー　：
　　　modelBuilder.Entity<モデル名>().HasKey(c => new { c.キー名１, c.キー名２ );
　　　
#####③データの自動生成
[DatabaseGenerated(DatabaseGeneratedOption.None)]　
　自動生成しない（主キーなどを自分の思い通り設定したい場合便利？）
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]　
　レコード追加時自動で割り振り
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]　
　追加更新時に自動で割り振り

#####④Not Null制約
　[Required]

#####⑤最大長
　[MaxLength(500)]

#####⑥楽観的排他制御
　[ConcurrencyCheck]：指定したフィールドが楽観的排他チェックの対処対象となる　　　　　　
　　　　　　　　　　　DbUpdateConcurrencyExceptionが発生
[Timestamp]：行が更新追加された際にタイムスタンプが保存される行
　　　　　　　排他チェックの対象となる→DbUpdateConcurrencyExceptionが発生
#####⑦シャドウ プロパティ
　モデルでは定義されてないけど、モデルのプロパティとして利用出来るよ！ってやつ？
 　　modelBuilder.Entity<クラス名>().Property<DateTime>("シャドウプロパティ名");
 
 利用方法
 　context.Entry(モデルが入った変数).Property("シャドウプロパティ名").CurrentValue = DateTime.Now;
 　var XXXX = context.XXXX.OrderBy(b => EF.Property<DateTime>(b, "シャドウプロパティ名"));
 　
 ~~使い道が今一わからん・・・？~~
 
#####⑧リレーションシップ
　https://docs.microsoft.com/ja-jp/ef/core/modeling/relationships
　
　ア．完全に定義されたリレーションシップ

```C#:sample.cs
	public class Blog
	{
	    public int BlogId { get; set; }
	    public string Url { get; set; }

	    public List<Post> Posts { get; set; }
	}

	public class Post
	{
	    public int PostId { get; set; }
	    public string Title { get; set; }
	    public string Content { get; set; }

	    public int BlogId { get; set; }
	    public Blog Blog { get; set; }
	}
```

　イ．外部キー プロパティ

```C#:sample.cs
	public class Blog
	{
	    public int BlogId { get; set; }
	    public string Url { get; set; }

	    public List<Post> Posts { get; set; }
	}

	public class Post
	{
	    public int PostId { get; set; }
	    public string Title { get; set; }
	    public string Content { get; set; }

	    public Blog Blog { get; set; }
	}
```
　ウ．単一のナビゲーション プロパティ

```C#:sample.cs
	public class Blog
	{
	    public int BlogId { get; set; }
	    public string Url { get; set; }

	    public List<Post> Posts { get; set; }
	}

	public class Post
	{
	    public int PostId { get; set; }
	    public string Title { get; set; }
	    public string Content { get; set; }
	}
```
　エ.データ注釈
　　①外部キーの名前が完全に定義されたリレーションシップのような命名に寄らない場合
　　[ForeignKey("外部キーの名前")]

```C#:sample.cs
		public class Blog
		{
		    public int BlogId { get; set; }
		    public string Url { get; set; }

		    public List<Post> Posts { get; set; }
		}

		public class Post
		{
		    public int PostId { get; set; }
		    public string Title { get; set; }
		    public string Content { get; set; }

		    public int BlogForeignKey { get; set; }

		    [ForeignKey("BlogForeignKey")]
		    public Blog Blog { get; set; }
		}
```
	②一つのモデルから同じモデルに二つのリンクが張られている場合
	　[InverseProperty("リンク先のモデルのプロパティ名")]

```C#:sample.cs
		public class Post
		{
		    public int PostId { get; set; }
		    public string Title { get; set; }
		    public string Content { get; set; }

		    public int AuthorUserId { get; set; }
		    public User Author { get; set; }

		    public int ContributorUserId { get; set; }
		    public User Contributor { get; set; }
		}

		public class User
		{
		    public string UserId { get; set; }
		    public string FirstName { get; set; }
		    public string LastName { get; set; }

		    [InverseProperty("Author")]
		    public List<Post> AuthoredPosts { get; set; }

		    [InverseProperty("Contributor")]
		    public List<Post> ContributedToPosts { get; set; }
		}
```

　オ．複合外部キー（Fluentを使うしかない？）
modelBuilder.Entity<多の方のモデル名>()
.HasOne(s => s.１の方のモデル名)
.WithMany(c => c.１の方のプロパティ名)
.HasForeignKey(s => new { s.多の方のプロパティ名, s.多の方のプロパティ名 });
            
　カ．多対多

```C#:sample.cs
	class MyContext : DbContext
	{
	    public DbSet<Post> Posts { get; set; }
	    public DbSet<Tag> Tags { get; set; }

	    protected override void OnModelCreating(ModelBuilder modelBuilder)
	    {
	        modelBuilder.Entity<PostTag>()
	            .HasKey(t => new { t.PostId, t.TagId });

	        modelBuilder.Entity<PostTag>()
	            .HasOne(pt => pt.Post)
	            .WithMany(p => p.PostTags)
	            .HasForeignKey(pt => pt.PostId);

	        modelBuilder.Entity<PostTag>()
	            .HasOne(pt => pt.Tag)
	            .WithMany(t => t.PostTags)
	            .HasForeignKey(pt => pt.TagId);
	    }
	}

	public class Post
	{
	    public int PostId { get; set; }
	    public string Title { get; set; }
	    public string Content { get; set; }

	    public List<PostTag> PostTags { get; set; }
	}

	public class Tag
	{
	    public string TagId { get; set; }

	    public List<PostTag> PostTags { get; set; }
	}

	public class PostTag
	{
	    public int PostId { get; set; }
	    public Post Post { get; set; }

	    public string TagId { get; set; }
	    public Tag Tag { get; set; }
	}
```

#####⑨INDEX
	modelBuilder.Entity<クラス名>().HasIndex(b => b.Indexを張るプロパティ名);
	ユニークにしたい場合.IsUnique()
	複数も可.HasIndex(p => new { p.プロパティ名１, p.プロパティ名２ });
	
#####⑩自動プロパティ・バッキングフィールド
　ＤＢから展開する場合は、アクセサを使わず直接バッキングフィールドに書き込まれる（ようだ）

```C#:sample.cs
public class Blog
{
    private string _url; //←コレの命名規則は_<camel-cased property name>　とかm_<camel-cased property name>

    public int BlogId { get; set; }

    public string Url
    {
        get { return _url; }
        set { _url = value; }
    }
}
```

#####⑪値の変換
　作例のEnumの名前の方で保存されるよってこった　使い道は・・・？？


```C#:sample.cs
	    public class Rider
		{
		   public int Id { get; set; }
		   public EquineBeast Mount { get; set; }
		}

		public enum EquineBeast
		{
		   Donkey,
		   Mule,
		   Horse,
		   Unicorn
		}
	protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Rider>()
        .Property(e => e.Mount)
        .HasConversion(
            v => v.ToString(),
            v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
}
```
#####⑫所有エンティティ型
　入れ子のように使える。実テーブルは一個

```C#:sample.cs
	[Owned]
	public class StreetAddress
	{
	    public string Street { get; set; }
	    public string City { get; set; }
	}
	public class Order
	{
	    public int Id { get; set; }
	    public StreetAddress ShippingAddress { get; set; }
	}
```

 OnModelCreatingに

```C#:sample.cs
            #region OwnsOne
            modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);
            #endregion

            #region OwnsOneString
            modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");
            #endregion

            #region ColumnNames
            modelBuilder.Entity<Order>().OwnsOne(
                o => o.ShippingAddress,
                sa =>
                {
                    sa.Property(p => p.Street).HasColumnName("ShipsToStreet");
                    sa.Property(p => p.City).HasColumnName("ShipsToCity");
                });
            #endregion
```

####以降はＤＢへの制約

#####⑭テーブル マッピング

```C#:sample.cs
  [Table("blogs")]
  [Table("blogs", Schema = "blogging")] //スキーマの設定
```
#####⑮nvarchar(max)列マッピング

```C#:sample.cs
 [Column("blog_id")]
```
#####⑯データの種類
　 [Column(TypeName = "varchar(200)")]　decimal(5, 2)　nvarchar(max)　DateTime
　 使える種類はＤＢによって異なる
#####⑰主キー
　　　　[Key]
　　　　命名したい場合

```C#:sample.cs
        modelBuilder.Entity<Blog>()
            .HasKey(b => b.BlogId)
            .HasName("PrimaryKey_BlogId");
```

#####⑱既定のスキーマ

```C#:sample.cs
   modelBuilder.HasDefaultSchema("blogging");
```
#####⑲計算列
　ＤＢに計算列は作られないが使い道が・・・？

```C#:sample.cs
	class MyContext : DbContext
	{
	    public DbSet<Person> People { get; set; }

	    protected override void OnModelCreating(ModelBuilder modelBuilder)
	    {
	        modelBuilder.Entity<Person>()
	            .Property(p => p.DisplayName)
	            .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
	    }
	}

	public class Person
	{
	    public int PersonId { get; set; }
	    public string FirstName { get; set; }
	    public string LastName { get; set; }
	    public string DisplayName { get; set; }
	}
```
#####⑳シーケンス
　※特定のテーブルに関連付けはされない

```C#:sample.cs
   modelBuilder.HasSequence<int>("OrderNumbers");
```

#####㉑既定値　初期値

```C#:sample.cs
	class MyContext : DbContext
	{
	    public DbSet<Blog> Blogs { get; set; }

	    protected override void OnModelCreating(ModelBuilder modelBuilder)
	    {
	        modelBuilder.Entity<Blog>()
	            .Property(b => b.Rating)
	            .HasDefaultValue(3);
	    }
	}

	public class Blog
	{
	    public int BlogId { get; set; }
	    public string Url { get; set; }
	    public int Rating { get; set; }
	}
```
※SQL フラグメントを利用することも出来る。

```C#:sample.cs
.HasDefaultValueSql("getdate()");
```
#####㉒インデックス


```C#:sample.cs
        modelBuilder.Entity<Blog>()
            .HasIndex(b => b.Url)
            .HasName("Index_Url");
```
#####㉓フィルター

```C#:sample.cs
        modelBuilder.Entity<Blog>()
            .HasIndex(b => b.Url)
            .HasFilter("[Url] IS NOT NULL");
```

#####㉔外部キー制約

```C#:sample.cs
	class MyContext : DbContext
	{
	    public DbSet<Blog> Blogs { get; set; }
	    public DbSet<Post> Posts { get; set; }

	    protected override void OnModelCreating(ModelBuilder modelBuilder)
	    {
	        modelBuilder.Entity<Post>()
	            .HasOne(p => p.Blog)
	            .WithMany(b => b.Posts)
	            .HasForeignKey(p => p.BlogId)
	            .HasConstraintName("ForeignKey_Post_Blog");
	    }
	}

	public class Blog
	{
	    public int BlogId { get; set; }
	    public string Url { get; set; }

	    public List<Post> Posts { get; set; }
	}

	public class Post
	{
	    public int PostId { get; set; }
	    public string Title { get; set; }
	    public string Content { get; set; }

	    public int BlogId { get; set; }
	    public Blog Blog { get; set; }
	}
```