Entity Framework 6でSQL Server 2000へアクセスする手順

本来、Entity Framework 6(以降、EF6）はSQL Server 2000には非対応（SQL Server 2005から対応）ですが、無理やり使用することができるようです。

大まかにいうと、一旦SQL Server 2008 Expressにデータをインポートして、SQL Serer 2008 Expressに対してEF6をセットアップし、Modelクラスが生成された後、Db接続情報をSQL Server 2000に切り替える、というものです。

例えば、古いClassic ASPシステムをASP.NETに置き換える際に「データベースは当面古いSQL Server 2000を使い続けなければならないが、ASP.NETではEntity Frameworkを使う必要がある」という場合にお試しください。

下記2点の手順を示します。

1. 既存のデータベースからCode FirstでModelクラスとDbContextクラスを自動生成する。
2. 生成されたクラスを用いてデータベースにアクセスする。

【試した環境】
- Visual Studio 2019
- SQL Server 2000(Windows 2000 Server上で動作)
- Windows 10 Pro(64bit)
- EntityFramework 6.4.4
- VB.NET
- ASP.NET MVC 5.2.7

:::note warn
今のところ私の環境では問題は起きていませんが、本来非対応のものですので、十分にテストしてご利用ください。
こちらの情報によれば、主に「APPLY」オペレータの処理に関する制限のようです。
https://stackoverflow.com/questions/25240192/entity-framework-6-supports-sql-server-2000
:::

:::note warn
ASP.NET Core(.NET 5)でEF Coreにしたい場合にも、似たようなやり方でいけるかもしれませんが未検証です。EF CoreはSQL Server 2012以降のみ対応で、SQL Server 2000からのデータのインポートはSQL Server 2008までしか対応していないので、何段階かインポートをリレーしないとダメだと思われます。個人的には、EF Coreはまだ発展途上の印象があり、.NET 5でもEF6が利用できることから、問題がないのならEF6を利用してよいと思います。
:::

#手順
##1. SQL Server 2008 Expressのインストール
こちらからインストールします。現在のOSのアーキテクチャに合ったもの（32bitならx86版、64bitならx64版）をダウンロードしてください。
https://www.microsoft.com/ja-jp/download/details.aspx?id=30438

認証方法は、現在のSQL Server 2000と併せます（Windows認証 or SQL Server認証）。
分からなかったら「SQL Server認証」を選択します。

##2. SQL Server 2000からのデータのインポート
スタートメニューから「データのエクスポートおよびインポート」を起動します。

データソース（インポート元）として、SQL Server 2000のサーバ情報を入力します。
SQL Server 2000のデータベース情報を入力します。

- データソース: SQL Server Native Client 10.0
- サーバー名: <SQL Server 2000のサーバ名>
- 認証: <ここでは「SQL Server認証を使用する」とします>
- ユーザーID: <ユーザーID>
- パスワード: <パスワード>
- データベース: <対象となるデータベース名>

次へ進み、今度は変換先として、今インストールしたSQL Server 2008のサーバーを選びます。

- 変換先: SQL Server Native Client 10.0
- サーバー名: <あなたのコンピュータ名>\SQLEXPRESS　※ここは設定によります
- 認証: SQL Server 2008インストール時に設定した内容
- データベース: <SQL Server 2000と同じデータベース名>

入力したら「新規作成」を押下し、データベースを新規作成します。
次へ進み、「完了」を押下すると、インポートが開始されます。

##3. EntityFrameworkをNuGetでインストール
- Visual Studioで対象となるソリューションを開くか、新規作成します。
- 「Entity」という名前のプロジェクトを新規追加します。名前はなんでも構いません。ぶっちゃけわざわざプロジェクトを作らずとも、ソリューションのメインプロジェクトに直接エンティティクラスを生成してもよいのですが、整理の為にプロジェクトを分けています。
- プロジェクトの「参照」フォルダを右クリックし、[NuGetパッケージの管理(N)...]をクリックします。
- "EntityFramework"を探し、説明の所に「Entity Framework 6(EF6)」と書かれているパッケージを「Entity」プロジェクトと、ソリューションのメインプロジェクトにインストールします。現在の最新バージョンは6.4.4です。

##4. データベースからDbContextおよびModelクラスを生成
下記手順をSQL Server 2000に対して行うと、「EF6はこのバージョンのSQL Serverに対応していません」というエラーが出て先へ進めません。その為の手順1.および2.となります。

- ソリューションエクスプローラのEntityプロジェクトの所で右クリックし、「追加｜新しい項目」を選びます。
- 左側のメニューから[データ]を選択し、[ADO.NET Entity Data Model]を選びます。
- インポートが完了したSQL Server 2008 Expressのデータベースを接続先として選びます。
- 「接続設定に名前を付けてApp.Configに保存」を選びます。
- 名称欄には、今回のデータベースを表す名前を入力します。HogeSystemデータベースなら、"HogeSystem"と入力します。すると、DbContextクラスとして「HogeSystemContext」クラスが生成されます。

- その後、モデルクラスを生成したいテーブル名にチェックを入れて、クラスを生成します。
- コンパイルが通るか試します。通らなければ、何か手順を間違えている可能性があります。

##5. テストコードを試す

ソリューションのメインプロジェクトに移ります。
メインプロジェクトの「参照」で右クリックし、Entityプロジェクトへの参照を追加します。
適当なテスト用のプログラムを作ります。

データベース名が「HogeSystem」、テーブルの１つが「Employees」だったとします。この辺は自分のシステムに置き換えてお読みください。

```vb
Imports Entity

Sub Test1()
	Dim db As New HogeSystemContext
	
	For Each emp In db.Employees
		Console.WriteLine(emp.Name)
	Next
End Sub
```

これをコンパイルして実行すると、「接続文字列名'HogeContext'がapp.configに見つからない」というエラーが表示されます。
手順4.で設定したデータベース情報は、EntityプロジェクトのApp.Configにしか保存されていない為です。これを、メインプロジェクトのApp.Config（ASP.NETの場合はweb.config）にコピーします。

```
  <connectionStrings>
    <add name="HogeSystemContext" connectionString="data source=<DBサーバ名>;initial catalog=<データベース名>;persist security info=True;user id=<ユーザーID>;password=<パスワード>;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>
```

上記部分をコピーします。

再度実行すると、今度は「'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' を読み込めませんでした。」というエラーが出ます。

原因は、EntityFramework.SqlServer.dllがコンパイル結果出力先（通常はDebugフォルダ）に存在しないというもので、恐らく、ソースコード上からこのDLLの機能を一切使っていない為、不要と判断されてしまっているのではと推測します。

こちらに解決策が載っていました。感謝。
https://qiita.com/atatago/items/a698806607b756b380fc

いくつか解決策があり、メインプロジェクトにEntityFramework.SqlServer.dllへの参照を追加して「ローカルコピー」をTrueにするというものもありましたが、せっかくEntityを別プロジェクトに分けたのですから、無駄な依存関係を追加したくはありません。

そこで、HogeSystemContextのコンストラクタ内でEntityFramework.SqlServer.dllの機能を意味もなく使うようにして、dllへの参照をコンパイラに伝えるようにします。

```vb
Partial Public Class HogeSystemContext
    Inherits DbContext

    Public Sub New()
        MyBase.New("name=HogeSystemContext")

        'なぜかEntityFramework.SqlServer.dllが発行先にデプロイされないので、ここで意味なくコードから参照して、デプロイされるようにしている。
        Dim instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance
    End Sub
```

ちなみにですが、上記の「MyBase.New("name=HogeSystemContext")」で与えているnameは、App.Configの接続先文字列情報に付けたnameです。ここで与えたnameを元に、接続先情報を探して自動的にDBに接続してくれるので、使う側は、単にこのHogeSystemContextをNewするだけです。

逆に言うと、接続先を変更したい場合、App.Configなりweb.configなりを変更したり、発行オプションで接続先情報を上書きしたりすれば良い事になります。

##6. SQL Server 2000に繋ぎ変える

```
  <connectionStrings>
    <add name="HogeSystemContext2008" connectionString="data source=<DBサーバ名>;initial catalog=<データベース名>;persist security info=True;user id=<ユーザーID>;password=<パスワード>;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
    <add name="HogeSystemContext" connectionString="data source=<SQL Server 2000のDBサーバ名>;initial catalog=<データベース名>;persist security info=False;user id=<ユーザーID>;password=<パスワード>;MultipleActiveResultSets=False;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>
```

上記のように、メインプロジェクトのApp.Config（又はweb.config）の接続文字列情報を、SQL Server 2000用に書き変えます。

persist security infoは通常Falseにした方が良いはずですが、なぜSQL Server 2008ではTrueになっているのかよくわかりません。SQL Server 2000でこのオプションに対応しているかわからないので、何か問題になりそうなら削除してしまっても良いかもしれません。

MultipleActiveResultSetsは、MARSというSQL Server独自の機能で、SQL Server 2000では非対応なのでFalseにしておきます。

書き変えが終わったら、再度実行します。すると、問題なくSQL Server 2000にアクセスできていることが確認できます。

#まとめ

EF6からSQL Server 2000を使用する方法をまとめました。
冒頭にも書いた通り、イレギュラーな方法です。一時しのぎとして利用し、なるべく早くSQL Server 2000以上にアップデートしましょう。