web-dev-qa-db-ja.com

EntityFramework7とAsp.Net5を使用してSQLストアドプロシージャを呼び出すにはどうすればよいですか

ここ数日、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をプロジェクトに追加しますが、すぐにエラーメッセージを表示し始めます(下のスクリーンショットを参照してください)。これを解決する方法がわかりません。

enter image description here

別のテストプロジェクトがあり、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 ProcsViewsを呼び出す機能がある場合、エラーを解決するにはどうすればよいですか(スクリーンショット)。

これを達成するためのベストプラクティスは何ですか?

5
Subrata Sarkar

私はあなたの問題を正しく理解することを望みます。データベースに既存のストアドプロシージャ(たとえば、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#コードは調整する必要はありません。

11
Oleg

DbContextにはDatabaseプロパティがあり、データベースへの接続を保持して、次の操作を実行できます。

context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar);

ただし、Web Apiアクションでこれを行うのではなく、コンテキストまたはコンテキストと相互作用するサービス/リポジトリにメソッドを追加することをお勧めします。次に、アクションでこのメソッドを呼び出すだけです。理想的には、すべてのSQL関連のものを1か所に保管する必要があります。

5
Chris Pratt

上記の答えと同じように、SqlQuery <>()の代わりにFromSQL()を使用することもできます。

context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);
1
TGarrett

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(); 
1
chri3g91

データベースファーストアプローチの場合、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);
0
Rohith