Entity Framework Core 1.1のHasField()とUsePropertyAccessMode()を使ってみた
Entity Framework Core 1.0 → 1.1における機能追加の1つとして「HasField()メソッドの追加」というものがあります。
Entity Frameworkでは、基本的に
「モデルクラス=データベース上のテーブル」
「モデルクラスのプロパティ=データベーステーブル上のカラム」
というマッピングを行います。
HasField()を利用すると「(クラス)フィールド」をデータベーステーブルカラムにマップすることが出来ます。
テスト環境
本投稿のテスト環境は以下の通りです。
C:\Users\ryuichi>dotnet --info .NET Command Line Tools (1.0.0-preview4-004110) Product Information: Version: 1.0.0-preview4-004110 Commit SHA-1 hash: 740a7fe3fd Runtime Environment: OS Name: Windows OS Version: 10.0.14393 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\1.0.0-preview4-004110
.NET Core Downloadsからダウンロードできるstableなバージョンではなく、デイリービルドされているGithub上のhttps://github.com/dotnet/cliから2016/11/23にダウンロードを行いました。
このバージョンは dotnet new で .csproj を生成します。
データベーステーブル定義
まず、今回使用するデータベーステーブルの定義は以下のようなものを想定します。
テーブル名は UserAccount で、列定義は「主キーである ID、氏名を表す FirstName / LastName、TwitterアカウントIDを表す ValidatedTwitterId」という構成です。ValidatedTwitterIdとは、確かにTwitter上に対象IDが存在すると検証したTwitterIDという意味です。
プロジェクトを作成
コマンドプロンプトを開き、dotnetコマンドによりプロジェクトを作成します。
ここでは、c:\Projects\dotNet\efCore11Exampleフォルダで以下のコマンドを実行しました。
dotnet new
.csprojに参照設定を追加
本サンプルではEntity Framework 1.1 / HttpClient を利用します。
csprojファイルは以下のように PackegeReference を追加しています。
// efCore11Example.csproj <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" /> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp1.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Include="**\*.cs" /> <EmbeddedResource Include="**\*.resx" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.NETCore.App"> <Version>1.0.1</Version> </PackageReference> <PackageReference Include="Microsoft.NET.Sdk"> <Version>1.0.0-alpha-20161104-2</Version> <PrivateAssets>All</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore"> <Version>1.1.0</Version> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"> <Version>1.1.0</Version> </PackageReference> <PackageReference Include="System.Net.Http"> <Version>4.3.0</Version> </PackageReference> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
.csprojを修正した後、「dotnet restore」を実行しておきましょう。
モデルクラスの実装
UserAccountモデルクラスを実装します。
// UserAccount.cs using System.Net.Http; using System.Threading.Tasks; namespace efCore11Example.Models { public class UserAccount { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } private string _validatedTwitterId; public async Task SetTwitterId(string twitterID) { using (var client = new HttpClient()) { string url = "https://twitter.com/" + twitterID; var response = client.GetAsync(url).Result; var stringResponse = await response.Content.ReadAsStringAsync(); if( !stringResponse.Contains("<form class=\"search-404\" action=\"https://twitter.com/search\" method=\"get\">") ) { this._validatedTwitterId = twitterID; } } } public string GetTwitterId() { return this._validatedTwitterId; } } }
UserAccountテーブルのカラムに該当する「Id / FirstName / LastName」は、通常通りのプロパティとして実装します。
ValidatedTwitterId列に対応するプロパティは定義せず、代わりに「validatedTwitterId」フィールドを用意します。
また、validatedTwitterIdフィールド値を設定するための「SetTwitterId()メソッド」を用意します。このメソッドは、twitterサイトにHTTPリクエストを行い、TwitterIDの存在チェックを行っています。あまり良い実装ではありませんが、Twitterに対して存在しないTiwtterIDのページをリクエストした際、
”レスポンスされたHTMLに「class="search-404"」というタグが含まれる”
という事柄を利用してTwitterIDの存在を確認しています。
DbContextクラスの実装
次に、DbContextクラスを継承したExampleDbContextクラスを実装します。
// ExampleDbContext.cs using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; namespace efCore11Example.Models { public class ExampleDbContext : DbContext { public DbSet<UserAccount> UserAccounts { get; set; } public ExampleDbContext() { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=tcp:rdexampledb2svr.database.windows.net,1433;Initial Catalog=RdExampleDb2;Persist Security Info=False;User ID=[user id];Password=[password];MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<UserAccount>() .Property<string>("ValidatedTwitterId") .HasField("_validatedTwitterId") .UsePropertyAccessMode(PropertyAccessMode.Field); } } }
DbSetとしてUserAccountsプロパティを定義します。
OnConfiguring()メソッドで接続先SQL Serverへの接続文字列を設定しました。ここでは私のAzure上のSQL Databaseへの接続文字列の設定を行っています。
そして、OnModelCreating()での実装が今回の肝になります。
UserAccountモデルクラスとデータベーステーブルのマッピングについて追加の情報を設定しています。
「.Property
「.HasField("validatedTwitterId").UsePropertyAccessMode(PropertyAccessMode.Field);」は、ValidatedTwitterIdプロパティに該当するモデルクラスの要素は「validatedTwitterId」であり、それは「フィールドとしてアクセス可能である」と定義しています。
以上で、モデルクラスおよびDbContextの準備が整いました。
ExampleDbContext / UserAccountを利用してDB操作を行う
main()メソッドに、ExampleDbContextを利用してUserAccountの追加 および 読み取り を行う実装を行うこととします。
// Program.cs using System; using efCore11Example.Models; namespace efCore11Example { class Program { static void Main(string[] args) { // レコードを追加 AddAccountUsers(); // レコードを読み取り ReadAccountUsers(); } // 2件のUserAccountをDBに追加します。 // 1件は不正なTwitterIDです。 public static void AddAccountUsers() { using(ExampleDbContext context = new ExampleDbContext()) { UserAccount userAccount = new UserAccount(); userAccount.Id = 1; userAccount.FirstName = "ryuichi"; userAccount.LastName = "daigo"; userAccount.SetTwitterId("ryuichi111std").Wait(); context.UserAccounts.Add(userAccount); UserAccount userAccount2 = new UserAccount(); userAccount2.Id = 2; userAccount2.FirstName = "fusei"; userAccount2.LastName = "account"; userAccount2.SetTwitterId("fuseinatwitterid").Wait(); context.UserAccounts.Add(userAccount2); context.SaveChanges(); } } // UserAccountテーブルから読み取ってコンソール出力を行います。 public static void ReadAccountUsers() { using(ExampleDbContext context = new ExampleDbContext()) { foreach( var userAccount in context.UserAccounts) { System.Console.WriteLine("氏名: " + userAccount.FirstName + userAccount.LastName); System.Console.WriteLine("TwitterID: " + userAccount.GetTwitterId()); System.Console.WriteLine("-----"); } } } } }
コード内コメントにもあるように2人の UserAccount を追加します。1人は存在するTwitterIdを設定し、もう1人は存在しないTwitterIdを設定しています。後者の 存在しないTiwtterId を指定したユーザーは UserAccount.SetTwitterId() 内の処理により_validatedTwitterIdフィールド には値が反映されません。
実行する
「dotnet run」コマンドでプログラムを実行します。
実行結果は、以下の通りです。
C:\Projects\dotNet\efCore11Example>dotnet run 氏名: ryuichidaigo TwitterID: ryuichi111std ----- 氏名: fuseiaccount TwitterID: -----
SQL Management Studioでデータベーステーブルを確認した結果は以下の画面キャプチャになります。