Avanade Beef でカスタムのデータ ソースを使ってみる
はじめに
Avanade Beef (以下、Beef) は ASP.NET Core をベースとした Web API の自動生成ツールです。
概要については以下のスライドもご覧ください。
Beef では、既定で以下のデータソースを扱うことができます。
- RDBMS (ストアドプロシージャまたは Entity Framework)
- Cosmos DB
- OData
これら以外のデータソースも、カスタムとして組み込むことができます。今回はサンプルとして、Microsoft Graph をデータソースとして Azure AD のすべてのユーザーを取得する API を作成する例を紹介します。
サンプル コード
実行手順
プロジェクトの作成
今回はデータ ソースを None としてプロジェクトを作成します。
dotnet new beef --company Karamem0 --appname SampleApplication --datasource None
Api および Business プロジェクトに Microsoft.Graph を追加します。
dotnet add package Microsoft.Graph
コードの修正
entity.beef.yaml
エンティティを定義します。ユーザーの一覧を返すため getAll: true とします。また、データソースはカスタムで実装するため autoImplement: None とします。
entityScope: Autonomous
appBasedAgentArgs: true
webApiAutoLocation: true
eventOutbox: None
entities:
- name: User
getAll: true
collection: true
collectionResult: true
webApiRoutePrefix: users
autoImplement: None
properties:
- name: Id
type: Guid
uniqueKey: true
- name: UserPrincipalName
type: string
- name: DisplayName
type: string
コードを自動生成します。
dotnet run all
Business/Data/UserData.cs
autoImplement: None としたため、自動生成される UserData.cs の実装は以下のようになります。
/// <summary>
/// Gets the <see cref="UserCollectionResult"/> that contains the items that match the selection criteria.
/// </summary>
/// <returns>The <see cref="UserCollectionResult"/>.</returns>
public Task<UserCollectionResult> GetAllAsync() => DataInvoker.Current.InvokeAsync(this, () => GetAllOnImplementationAsync());
ここで呼び出される GetAllOnImplementationAsync メソッドを自前で実装する必要があります。UserData.cs をパーシャル クラスとして追加します。Generated フォルダーにある UserData.cs は直接修正しないでください。コンストラクターを追加して GraphServiceClient インスタンスを受け取るようにします。GetAllOnImplementationAsync メソッドで GraphServiceClient インスタンスを使って取得したユーザーの一覧を返ります。
namespace Karamem0.SampleApplication.Business.Data
{
/// <summary>
/// Provides the <see cref="User"/> data access.
/// </summary>
public partial class UserData : IUserData
{
private readonly GraphServiceClient _graphServiceClient;
/// <summary>
/// Initializes a new instance of the <see cref="UserData"/> class.
/// </summary>
public UserData(GraphServiceClient graphServiceClient) : this()
{
_graphServiceClient = graphServiceClient;
}
/// <summary>
/// Gets the <see cref="UserCollectionResult"/> that contains the items that match the selection criteria.
/// </summary>
/// <returns>The <see cref="UserCollectionResult"/>.</returns>
public async Task<UserCollectionResult> GetAllOnImplementationAsync()
{
var __result = await _graphServiceClient.Users.Request().GetAsync();
return new UserCollectionResult(__result.Select(_ => new Entities.User()
{
Id = new Guid(_.Id),
UserPrincipalName = _.UserPrincipalName,
DisplayName = _.DisplayName,
}));
}
}
}
Api/Startup.cs
UserData.cs に DI で GraphServiceClient インスタンスを渡せるように ConfigureServices メソッドを構成します。今回はサンプルのため、わかりやすさを優先して Client Credentials Grant を使用していますが、実際の運用では推奨しません。通常は AddMicrosoftGraph メソッドで構成することをおすすめします。
services.AddSingleton<GraphServiceClient>(services =>
{
var credential = new ClientSecretCredential(
_config.GetValue<string>("MicrosoftGraph:TenantId"),
_config.GetValue<string>("MicrosoftGraph:ClientId"),
_config.GetValue<string>("MicrosoftGraph:ClientSecret"));
var scopes = new[] { "https://graph.microsoft.com/.default" };
var client = new GraphServiceClient(credential);
return client;
});
Api/webapisettings.json
Azure AD アプリケーションを登録し、その情報を webapisettings.json に設定します。
"MicrosoftGraph": {
"TenantId": "{{tenent-id}}",
"ClientId": "{{client-id}}",
"ClientSecret": "{{tenent-secret}}"
},
実行結果
プロジェクトをデバッグ実行します。ブラウザーで http://localhost:5000/swagger を起動し、/users を実行して結果が取得できることを確認します。
おわりに
カスタムのデータソースを使用した場合でも、データアクセス層のみを実装すればよいので、簡単に実装できます。既定のデータソースを使用する場合でも、ロジックをカスタマイズしたいときは同様のテクニックを使用するため、ぜひ覚えておきたいです。
Discussion