Целесообразно ли объединять разных провайдеров IQueryable в одного? - PullRequest
0 голосов
/ 27 октября 2018

Введение в дилемму

Давайте предположим, что я хочу построить API OData для доступа к EDM следующим образом:

public class ApiUser
{
    public Guid Id { get; set; }
    public string UserName { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public ICollection<ApiTransaction> Transactions { get; set; }
}

public class ApiTransaction
{
    public Guid Id { get; set; }
    public DateTime TimeStamp { get; set; }
    public decimal Amount { get; set; }

    public ApiUser Account { get; set; }
}

Два объекта с отношением один ко многими навигационные свойства как таковые.Когда я должен был классически создать API для этого, учитывая, что мое хранилище поддерживается им (например, SQL Server), я взял свой любимый ORM, такой как EntityFramework, и построил мой API следующим образом.

Реализация ODataКонечная точка легко достигается, поскольку есть несколько пакетов, которые могут взять IQueryable из моего ORM и применить к нему язык запросов OData -> таким образом, я в основном только что создал прокси, который отображает интерфейс OData прямо в SQLБаза данных сервера.Довольно опрятно.

Но сейчас все в моде на микросервисах, и мы хотим разделить наши магазины.Поскольку транзакции являются их собственным бизнес-доменом, они находятся в разных сервисах.Может быть, они хранятся совершенно по-другому.Один находится в базе данных SQL, другой в Космосе, а другие объекты могут быть в Redis и т. Д. В конце концов, самый большой общий знаменатель этих служб репозитория состоит в том, что все они предоставляют клиента, который предоставляет свои модели.как IQueryable <>, и наши модели персистентности будут больше похожи на это:

public class DbUser
{
    public Guid Id { get; set; }
    public string UserName { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class DbTransaction
{
    public Guid Id { get; set; }
    public DateTime TimeStamp { get; set; }
    public decimal Amount { get; set; }

    public Guid AccountId { get; set; }
}

Обратите внимание, что больше нет свойств навигации.Наш API должен по-прежнему разрешать запросы OData для всех сущностей, но без свойств навигации это невозможно сделать без реализации каждого случая свойства навигации и его конкретной обработки.

Поэтому моя цель состоит в том, чтобыконечная точка OData с навигационными свойствами, подобными первому фрагменту, но они сохраняются в потенциально разных провайдерах с такими моделями, как второй фрагмент.

Мой запланированный подход

Познакомьтесь с вымышленным1021 * CombinedQueryableContextBuilder :

var builder = new CombinedQueryableContextBuilder();

builder.RegisterQueryableProvider<UserStoreClient>((entityMap) =>
    entityMap.Add<ApiUser, DbUser>((userStoreClient) => userStoreClient.Users));
builder.RegisterQueryableProvider<TransactionStoreClient>((entityMap) =>
    entityMap.Add<ApiTransaction, DbTransaction>((transactionStoreClient) => transactionStoreClient.Transactions));

var combinedQueryableContext = builder.BuildByConvention(conventionOptions => ...);

var apiUsers = combinedQueryableContext.GetEntitySet<ApiUser>();

var millionaires = apiUsers.Where(user => user.Transaction.Sum(tx => tx.Amount) >= 1_000_000m).ToList();

Чтобы дать некоторый контекст относительно того, что он должен делать: у меня есть веб-API, который должен предоставить конечную точку контекста odata, которая ссылается на все модели Api со свойствами навигациимежду ними.Модели могут храниться в разных контекстах / провайдерах, поэтому теперь существует объединитель, который выполняет черную магию и запрашивает несколько разных репозиториев, чтобы удовлетворить потребности в навигационных свойствах между ними.Объединитель может использовать соглашения, чтобы выяснить, как реализовать выражения.

Я мало знаю о провайдерах Queryable, и деревья выражений - это то, что мне еще предстоит полностью понять.(Для меня то, что делают ORM, для меня все еще остается черным искусством, но я готов потратить время на изучение)

Вопросы

Есть ли пакет, который уже делает это?

Возможно ли реализовать то, что я описал выше?

И если это так, то практично ли это для крупномасштабных приложений?(Я не имею в виду ничего особенно против этого, но, возможно, есть некоторые подводные камни, которые приводят к тому, что выполнение запросов исчисляется миллиардами, которых нелегко избежать при некотором планировании выполнения и просто абортов, когда что-то достигает определенного порога / предела.также не много знаю о внутренностях БД, и этот объединитель в основном является БД, которая использует несколько других БД в качестве источника)

...