ここ数日、Stored Procedure
を使用してWeb API
コントローラーメソッド内からEntityFramework 7
を呼び出す方法に関するチュートリアルを探しています。
私が経験したすべてのチュートリアルは、それを逆に示しています。つまり、Code First
アプローチです。しかし、私はすでにデータベースを用意しており、それを使用してWeb API
を構築する必要があります。さまざまなビジネスロジックがすでにストアドプロシージャとビューとして記述されており、WebAPIからそれらを使用する必要があります。
質問1:これはDatabase First
アプローチをEF7
で続行し、上記のようなデータベースオブジェクトを消費することは可能ですか?
次のNuGet
コマンドを使用して、EntityFramework 6.1.3
をパッケージにインストールしました。
install-package EntityFramework
これはバージョン6.1.3をプロジェクトに追加しますが、すぐにエラーメッセージを表示し始めます(下のスクリーンショットを参照してください)。これを解決する方法がわかりません。
別のテストプロジェクトがあり、project.json
に次のような2つのエントリが表示されます。
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", "EntityFramework.MicrosoftSqlServer.Design": "7.0.0-rc1-final",
ただし、Nu-Get
パッケージマネージャーで検索していると、このバージョンが表示されません。 6.1.3のみが登場します。
私の主な目的は、既存のデータベースから既に作成されたストアドプロシージャとビューを使用することです。
1)ADO.Net
を使用したくないので、ORM
を使用してEntityFramework
を使用したい
2)EntityFramework 6.1.3
に既存のデータベースからStored Procs
とViews
を呼び出す機能がある場合、エラーを解決するにはどうすればよいですか(スクリーンショット)。
これを達成するためのベストプラクティスは何ですか?
私はあなたの問題を正しく理解することを望みます。データベースに既存のストアドプロシージャ(たとえば、dbo.spGetSomeData
)があります。これは、いくつかのフィールドを持ついくつかのアイテムのリストを返し、WebAPIメソッドからデータを提供する必要があります。
実装は次のようになります。 emptyDbContext
のように定義できます:
public class MyDbContext : DbContext
{
}
データベースへの接続文字列を使用してappsettings.json
を定義します
{
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
}
MyDbContext
をに追加するにはMicrosoft.Extensions.DependencyInjection
を使用する必要があります
public class Startup
{
// property for holding configuration
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
.AddEnvironmentVariables();
// save the configuration in Configuration property
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options => {
options.UseSqlServer(Configuration["ConnectionString"]);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
}
}
これで、WebApiアクションを次のように実装できます。
[Route("api/[controller]")]
public class MyController : Controller
{
public MyDbContext _context { get; set; }
public MyController([FromServices] MyDbContext context)
{
_context = context;
}
[HttpGet]
public async IEnumerable<object> Get()
{
var returnObject = new List<dynamic>();
using (var cmd = _context.Database.GetDbConnection().CreateCommand()) {
cmd.CommandText = "exec dbo.spGetSomeData";
cmd.CommandType = CommandType.StoredProcedure;
// set some parameters of the stored procedure
cmd.Parameters.Add(new SqlParameter("@someParam",
SqlDbType.TinyInt) { Value = 1 });
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
var retObject = new List<dynamic>();
using (var dataReader = await cmd.ExecuteReaderAsync())
{
while (await dataReader.ReadAsync())
{
var dataRow = new ExpandoObject() as IDictionary<string, object>;
for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++) {
// one can modify the next line to
// if (dataReader.IsDBNull(iFiled))
// dataRow.Add(dataReader.GetName(iFiled), dataReader[iFiled]);
// if one want don't fill the property for NULL
// returned from the database
dataRow.Add(
dataReader.GetName(iFiled),
dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {}
);
}
retObject.Add((ExpandoObject)dataRow);
}
}
return retObject;
}
}
}
上記のコードは、exec dbo.spGetSomeData
を使用して実行し、dataRaderを使用してすべての結果を読み取り、そこにdynamic
オブジェクトに保存します。 $.ajax
からapi/My
呼び出しを行う場合は、JavaScriptコードで直接使用できるdbo.spGetSomeData
から返されるデータを取得します。上記のコードは非常に透過的です。 dbo.spGetSomeData
によって返されるデータセットのフィールドの名前は、JavaScriptコードのプロパティの名前になります。 C#コード内のエンティティクラスを管理する必要はありません。C#コードには、ストアドプロシージャから返されるフィールドの名前がありません。したがって、dbo.spGetSomeData
のコードを拡張/変更する場合(名前を変更する場合)フィールド、新しいフィールドの追加)JavaScriptコードのみを調整する必要があり、C#コードは調整する必要はありません。
DbContext
にはDatabase
プロパティがあり、データベースへの接続を保持して、次の操作を実行できます。
context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar);
ただし、Web Apiアクションでこれを行うのではなく、コンテキストまたはコンテキストと相互作用するサービス/リポジトリにメソッドを追加することをお勧めします。次に、アクションでこのメソッドを呼び出すだけです。理想的には、すべてのSQL関連のものを1か所に保管する必要があります。
上記の答えと同じように、SqlQuery <>()の代わりにFromSQL()を使用することもできます。
context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);
MySQLコネクタとEntityFrameworkコア2.0の使用
私の問題は、fxのような例外が発生していたことでした。 Ex.Message = "必要な列 'body'が 'FromSql'操作の結果に存在しませんでした。"したがって、この方法でストアドプロシージャを介して行をフェッチするには、この特定の呼び出しにすべてのデータが必要ない場合でも、DBSetが関連付けられているエンティティタイプのすべての列を返す必要があります。
var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList();
またはパラメータと
var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList();
データベースファーストアプローチの場合、Scaffold-DbContextコマンドを使用する必要があります
NugetパッケージMicrosoft.EntityFrameworkCore.ToolsおよびMicrosoft.EntityFrameworkCore.SqlServer.Designをインストールします
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
しかし、それはあなたのストアドプロシージャを取得しません。それはまだ作業中であり、問題を追跡しています #245
ただし、ストアドプロシージャを実行するには、RAW SQLクエリを実行する FromSql メソッドを使用します。
例えば.
var products= context.Products
.FromSql("EXECUTE dbo.GetProducts")
.ToList();
パラメータで使用するには
var productCategory= "Electronics";
var product = context.Products
.FromSql("EXECUTE dbo.GetProductByCategory {0}", productCategory)
.ToList();
または
var productCategory= new SqlParameter("productCategory", "Electronics");
var product = context.Product
.FromSql("EXECUTE dbo.GetProductByName @productCategory", productCategory)
.ToList();
RAWSQLクエリまたはストアドプロシージャの実行には特定の制限があります。INSERT/ UPDATE/DELETEには使用できません。 INSERT、UPDATE、DELETEクエリを実行する場合は、ExecuteSqlCommandを使用します
var categoryName = "Electronics";
dataContext.Database
.ExecuteSqlCommand("dbo.InsertCategory @p0", categoryName);