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