TokugawaTakeshi / CrossPlatformOrganizerAppplication

0 stars 0 forks source link

`PersonLocalEF_SQLiteGateway.RetrieveSelection`の最適化した実装 #8

Closed TokugawaTakeshi closed 1 year ago

TokugawaTakeshi commented 2 years ago

RetrieveSelectionメソッドは、

  1. データベースにある全ての項目の数を取得し
  2. フィールディングを行い
  3. フィールディング御の項目数を算出しなければいけません。

モックゲートウェイも場合は実装は下記通りでした。

public class PersonMockGateway : IPersonGateway
{

  private readonly MockDataSource mockDataSource = MockDataSource.getInstance();

  // ...

  public Task<IPersonGateway.SelectionRetrieving.ResponseData> RetrieveSelection(
    IPersonGateway.SelectionRetrieving.RequestParameters requestParameters
  )
  {

    return MockGatewayHelper.SimulateDataRetrieving<
      IPersonGateway.SelectionRetrieving.RequestParameters,
      IPersonGateway.SelectionRetrieving.ResponseData
    >(
      requestParameters: null,
      getResponseData: () => {

        List<Person> filteredItems;

        if (!String.IsNullOrEmpty(requestParameters.FilteringByName))
        {
          filteredItems = mockDataSource.People.Where(
            Person => Person.Name.Contains(requestParameters.FilteringByName)
          ).ToList();
        }
        else
        {
          filteredItems = mockDataSource.People;
        }

        List<Person> itemsActualForRequestedPaginationPage = PaginationHelper.SplitListToPaginationCollection(
          filteredItems, requestParameters.ItemsCountPerPaginationPage
        ).PagesContent[(int)requestParameters.PaginationPageNumber];

        return new IPersonGateway.SelectionRetrieving.ResponseData(
          totalItemsCount: Convert.ToUInt32(mockDataSource.People.Count),
          totalItemsCountInSelection: Convert.ToUInt32(filteredItems.Count),
          selectionItemsOfSpecifiedPaginationPage: itemsActualForRequestedPaginationPage
        );
      },
      new MockGatewayHelper.SimulationOptions
      {
        MinimalPendingPeriod__Seconds = 1,
        MaximalPendingPeriod__Seconds = 2,
        MustSimulateError = false,
        GatewayName = nameof(PersonMockGateway),
        TransactionName = "RetrievingOfSelection"
      }
    );
  }

  // ...
}

PersonLocalEF_SQLiteGateway内同じメソッドを実装する上で、データベースとの取引の数を最少化しなければいけないでしょう。最適化を考慮している実装の案内に就いては案内を待っています。

gummoni commented 2 years ago

Entity frameworkは意外とシンプルに実データを要求された時のみデータベースにアクセスします。

具体的にはDbContextに対してLINQを使い、欲しいデータだけを取得していきます。

//例:10件スキップした先の20件分データを取得する方法
var data = PeopleModels.Skip(10).Take(20).ToArray();

イメージとしては、LINQ文がそのままSQL文になっていると思って良いと思います。

TokugawaTakeshi commented 2 years ago

@gummoni

いかがですか?

public Task<IPersonGateway.SelectionRetrieving.ResponseData> RetrieveSelection(
  IPersonGateway.SelectionRetrieving.RequestParameters requestParameters
)
{

  List<PersonModel> filteredItems;

  if (String.IsNullOrEmpty(requestParameters.FilteringByName))
  {
    filteredItems = _dataBaseContext.PeopleModels.ToList();
  }
  else
  {
    filteredItems = _dataBaseContext.PeopleModels.Where(
      personModel => personModel.Name.Contains(requestParameters.FilteringByName)
    ).ToList(); 
  }

  return Task.FromResult(new IPersonGateway.SelectionRetrieving.ResponseData(
    totalItemsCount: (uint)_dataBaseContext.PeopleModels.Count(),
    totalItemsCountInSelection: (uint)filteredItems.Count,
    selectionItemsOfSpecifiedPaginationPage: filteredItems.
      Skip((int)(requestParameters.PaginationPageNumber * requestParameters.ItemsCountPerPaginationPage)).
      Take((int)requestParameters.ItemsCountPerPaginationPage).
      Select(_ => (Person)_).ToList())
  );
}

個人的に気に成っている事は

  1. 先に文字列が空ではないか、確認したいですが、if (requestParameters.FilteringByName.Length > 0)だと、例外が投げられる恐れがあります。どうすれば良いですか?if (requestParameters.FilteringByName?.Length > 0)で良いですか?
  2. 全てのゲートウェイはList中心実装されていますが、データの取り引き中常に要素数は固定ですから、将来的に全てのゲートウェイ内のListを配列に置き換える事はいかがでしょうか?Listは配列よりパフォーマンスを落としていますから、そう思いました。
gummoni commented 2 years ago

1つ目の文字が空か判定する時の書き方は

if (!String.IsNullOrEmpty(requestParameters.FilteringByName))

とすると良いです。

2つ目のListの処理速度ですが、C#の実装はうまく良くできているので配列型とList型で大幅な速度差はないので 最適化するタイミングでList型からArray形に変える方法で良いかもしれません。 もし、Arrayにする場合は、ToListをToArrayに代えるだけでOKです。

https://qiita.com/rait0/items/14e71ebd8ae26b13bbab

TokugawaTakeshi commented 1 year ago

@gummoni

御説明、有難う。

RetrieveSelectionは以上です。