#c# #.net #graphql
Вопрос:
Я использую свою мутацию, чтобы отправить один единственный «ввод для создания». Но мне нужно создать список из 480 объектов, возможно, в большом количестве.
модель (на стороне сервера):
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
namespace GraphQL.Domain.Models
{
public partial class ShopCreditTransaction
{
[Key][Column("Id")]
public int Id { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime CreatedAt { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime ModifiedAt { get; set; }
public string ApiTransactionId { get; set; }
public string ReferenceId { get; set; }
public string ShopStatus { get; set; }
public string CustomerFirstName { get; set; }
public string CustomerLastName { get; set; }
public decimal OriginalAmount { get; set; }
public decimal CurrentAmount { get; set; }
public decimal RevokedAmount { get; set; }
public int CurrencyId { get; set; } //FOREIGN KEY REFERENCES[db_owner].[Currency] (CurrencyID),
public string ApiCustomerId { get; set; }
public string ApiCreditAccountId { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? OrderDate { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? DeliveryDate { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? InvoiceDate { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? ReversalDate { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? ReversalEntryDate { get; set; }
[Column(TypeName = "smalldatetime")]
public DateTime? ReversalBookedDate { get; set; }
public string Md5Hash { get; set; }
}
}
Это модель по умолчанию (как и БД позади).
тип ввода (на стороне сервера):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GraphQL.Domain.InputTypes.ShopCredit
{
public class CreateShopCreditTransactionInput
{
public CreateShopCreditTransactionInput() { }
public CreateShopCreditTransactionInput(DateTime createdAt, DateTime modifiedAt, string apiTransactionId, string referenceId, string shopStatus, string apiCustomerId, string customerFirstName, string customerLastName, string apiCreditAccountId, decimal currentAmount, decimal originalAmount, decimal revokedAmount, int currencyId, DateTime? orderDate, DateTime? deliveryDate, DateTime? invoiceDate, DateTime? reversalDate, DateTime? reversalEntryDate, DateTime? reversalBookedDate, string md5Hash)
{
CreatedAt = createdAt;
ModifiedAt = modifiedAt;
ApiTransactionId = apiTransactionId;
ReferenceId = referenceId;
ShopStatus = shopStatus;
ApiCustomerId = apiCustomerId;
CustomerFirstName = customerFirstName;
CustomerLastName = customerLastName;
ApiCreditAccountId = apiCreditAccountId;
CurrentAmount = currentAmount;
OriginalAmount = originalAmount;
RevokedAmount = revokedAmount;
CurrencyId = currencyId;
OrderDate = orderDate;
DeliveryDate = deliveryDate;
InvoiceDate = invoiceDate;
ReversalDate = reversalDate;
ReversalEntryDate = reversalEntryDate;
ReversalBookedDate = reversalBookedDate;
Md5Hash = md5Hash;
}
public DateTime CreatedAt { get; set; }
public DateTime ModifiedAt { get; set; }
public string ApiTransactionId { get; set; }
public string ReferenceId { get; set; }
public string ShopStatus { get; set; }
public string ApiCustomerId { get; set; }
public string CustomerFirstName { get; set; }
public string CustomerLastName { get; set; }
public string ApiCreditAccountId { get; set; }
public decimal CurrentAmount { get; set; }
public decimal OriginalAmount { get; set; }
public decimal RevokedAmount { get; set; }
public int CurrencyId { get; set; }
public DateTime? OrderDate { get; set; }
public DateTime? DeliveryDate { get; set; }
public DateTime? InvoiceDate { get; set; }
public DateTime? ReversalDate { get; set; }
public DateTime? ReversalEntryDate { get; set; }
public DateTime? ReversalBookedDate { get; set; }
public string Md5Hash { get; set; }
}
}
Only the full input type for a create.
mutation type (server side):
using GraphQL.Domain.InputTypes.ShopCredit;
using GraphQL.Domain.Models;
using GraphQL.Infra.Data.Context;
using HotChocolate;
using HotChocolate.Data;
using HotChocolate.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace GraphQL.API.MutationTypes
{
[ExtendObjectType("Mutation")]
public class ShopCreditTransactionMutations : IShopCreditTransactionMutations
{
public async Task<ShopCreditTransaction> CreateShopCreditTransaction(CreateShopCreditTransactionInput createShopCreditTransactionInput)
{
var context = new ToolsDbContextFactory().CreateDbContext();
ShopCreditTransaction newTransaction = new ShopCreditTransaction()
{
CreatedAt = createShopCreditTransactionInput.CreatedAt,
ModifiedAt = createShopCreditTransactionInput.ModifiedAt,
ApiTransactionId = createShopCreditTransactionInput.ApiTransactionId,
ReferenceId = createShopCreditTransactionInput.ReferenceId,
ShopStatus = createShopCreditTransactionInput.ShopStatus,
ApiCustomerId = createShopCreditTransactionInput.ApiCustomerId,
CustomerFirstName = createShopCreditTransactionInput.CustomerFirstName,
CustomerLastName = createShopCreditTransactionInput.CustomerLastName,
ApiCreditAccountId = createShopCreditTransactionInput.ApiCreditAccountId,
CurrentAmount = createShopCreditTransactionInput.CurrentAmount,
OriginalAmount = createShopCreditTransactionInput.OriginalAmount,
RevokedAmount = createShopCreditTransactionInput.RevokedAmount,
CurrencyId = createShopCreditTransactionInput.CurrencyId,
OrderDate = createShopCreditTransactionInput.OrderDate,
DeliveryDate = createShopCreditTransactionInput.DeliveryDate,
InvoiceDate = createShopCreditTransactionInput.InvoiceDate,
ReversalDate = createShopCreditTransactionInput.ReversalDate,
ReversalEntryDate = createShopCreditTransactionInput.ReversalEntryDate,
ReversalBookedDate = createShopCreditTransactionInput.ReversalBookedDate,
Md5Hash = createShopCreditTransactionInput.Md5Hash,
};
var createdTransaction = context.ShopCreditTransactions.Add(newTransaction);
await context.SaveChangesAsync();
return createdTransaction.Entity;
}
}
}
The mutation type with embedded input type.
small entity (client side):
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CommonTemp.Payments.ShopCredit.Models
{
public class ShopCreditTransactionSyncCheck
{
[Key][Column("Id")]
public int Id { get; set; }
public string ApiTransactionId { get; set; }
public string ShopStatus { get; set; }
public string Md5Hash { get; set; }
}
}
A small query model (just the important fields).
input entity (client side):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CommonTemp.Payments.ShopCredit.Models
{
public class CreateShopCreditTransactionInput
{
public DateTime CreatedAt { get; set; }
public DateTime ModifiedAt { get; set; }
public string ApiTransactionId { get; set; }
public string ReferenceId { get; set; }
public string ShopStatus { get; set; }
public string ApiCustomerId { get; set; }
public string CustomerFirstName { get; set; }
public string CustomerLastName { get; set; }
public string ApiCreditAccountId { get; set; }
public decimal CurrentAmount { get; set; }
public decimal OriginalAmount { get; set; }
public decimal RevokedAmount { get; set; }
public int CurrencyId { get; set; }
public DateTime? OrderDate { get; set; }
public DateTime? DeliveryDate { get; set; }
public DateTime? InvoiceDate { get; set; }
public DateTime? ReversalDate { get; set; }
public DateTime? ReversalEntryDate { get; set; }
public DateTime? ReversalBookedDate { get; set; }
public string Md5Hash { get; set; }
public CreateShopCreditTransactionInput() { }
public CreateShopCreditTransactionInput(DateTime createdAt, DateTime modifiedAt, string apiTransactionId, string referenceId, string shopStatus, string apiCustomerId, string customerFirstName, string customerLastName, string apiCreditAccountId, decimal currentAmount, decimal originalAmount, decimal revokedAmount, int currencyId, DateTime? orderDate, DateTime? deliveryDate, DateTime? invoiceDate, DateTime? reversalDate, DateTime? reversalEntryDate, DateTime? reversalBookedDate, string md5Hash)
{
CreatedAt = createdAt;
ModifiedAt = modifiedAt;
ApiTransactionId = apiTransactionId;
ReferenceId = referenceId;
ShopStatus = shopStatus;
ApiCustomerId = apiCustomerId;
CustomerFirstName = customerFirstName;
CustomerLastName = customerLastName;
ApiCreditAccountId = apiCreditAccountId;
CurrentAmount = currentAmount;
OriginalAmount = originalAmount;
RevokedAmount = revokedAmount;
CurrencyId = currencyId;
OrderDate = orderDate;
DeliveryDate = deliveryDate;
InvoiceDate = invoiceDate;
ReversalDate = reversalDate;
ReversalEntryDate = reversalEntryDate;
ReversalBookedDate = reversalBookedDate;
Md5Hash = md5Hash;
}
}
}
Yeah, the full input type on client (for create).
Now I’m using Banana-Cake-Pop to tryout my query strings…
single create input (client side) — does work:
mutation {
createShopCreditTransaction(createShopCreditTransactionInput: {
createdAt: "2017-03-17",
modifiedAt: "2017-03-17",
apiTransactionId: "DDDDD",
referenceId: "-4",
shopStatus: "LIEFERUNG_MELDEN",
customerFirstName: "David",
customerLastName: "Tennant",
currentAmount: 666.0000,
originalAmount: 666.0000,
revokedAmount: 0,
currencyId: 1,
apiCustomerId: "-4",
apiCreditAccountId: "-4",
orderDate: null,
deliveryDate: null,
invoiceDate: null,
reversalDate: null,
reversalEntryDate: null,
reversalBookedDate: null
})
{
id,
md5Hash
}
}
mutliply создавать входные данные (на стороне клиента) — не работает:
mutation {
createShopCreditTransaction(createShopCreditTransactionInput: [
{
createdAt: "2021-07-20",
modifiedAt: "2021-07-20",
apiTransactionId: "AAAAAA",
referenceId: "I000457772",
shopStatus: "LIEFERUNG_MELDEN",
apiCustomerId: "9423104372",
customerFirstName: "Ralf",
customerLastName: "Ratenkauf",
apiCreditAccountId: "9341040827",
currentAmount: 100.0000,
originalAmount: 100.0000,
revokedAmount: 0.0000,
currencyId: 1,
orderDate: "2021-07-16",
deliveryDate: null,
invoiceDate: null,
reversalDate: null,
reversalEntryDate: null,
reversalBookedDate: null,
md5Hash: "f4162b9b7e4636864ad9e3e995108d45"
},
{
createdAt: "2021-07-20",
modifiedAt: "2021-07-20",
apiTransactionId: "BBBBBB",
referenceId: "I000457772",
shopStatus: "LIEFERUNG_MELDEN",
apiCustomerId: "9423104372",
customerFirstName: "Ralf",
customerLastName: "Ratenkauf",
apiCreditAccountId: "9341040827",
currentAmount: 200.0000,
originalAmount: 200.0000,
revokedAmount: 0.0000,
currencyId: 1,
orderDate: "2021-07-16",
deliveryDate: null,
invoiceDate: null,
reversalDate: null,
reversalEntryDate: null,
reversalBookedDate: null,
md5Hash: "e3162b9b7e4636864ad9e3e995108d46"
},
{
createdAt: "2021-07-20",
modifiedAt: "2021-07-20",
apiTransactionId: "CCCCCC",
referenceId: "I000457772",
shopStatus: "LIEFERUNG_MELDEN",
apiCustomerId: "9423104372",
customerFirstName: "Ralf",
customerLastName: "Ratenkauf",
apiCreditAccountId: "9341040827",
currentAmount: 300.0000,
originalAmount: 300.0000,
revokedAmount: 0.0000,
currencyId: 1,
orderDate: "2021-07-16",
deliveryDate: null,
invoiceDate: null,
reversalDate: null,
reversalEntryDate: null,
reversalBookedDate: null,
md5Hash: "d2162b9b7e4636864ad9e3e995108d47"
},
])
{
id,
apiTransactionId,
shopStatus,
md5Hash
}
}
Нужно ли мне менять модели на стороне сервера (на массив/коллекцию)?
Или мой синтаксис [bulk] неверен? И каков будет правильный синтаксис?
Есть ли способ обойтись без использования клубничного коктейля (hotchoclate), потому что у меня нет .NET-5 (только 4.xx)?
Это просто дешевый вариант магазина, для загрузки транзакций по API и проверки/синхронизации с базой данных.
Спасибо за каждую помощь!
Ответ №1:
Я действительно не могу помочь вам с частью C#, но в целом ввод мутации может быть либо списком, либо одним элементом. Самое забавное, что если это список, то он принимает отдельные элементы. Чтобы проиллюстрировать это:
type Mutation {
takesSingle(arg: String!): String
takesList(arg: [String!]!): String
}
query {
takesSingle(arg: "x") # works!
takesSingle(arg: ["x", "x"]) # errors!
takesList(arg: "x") # works!
takesList(arg: ["x", "x"]) # works!
}
Это означает, что в GraphQL нет массовой операции, которую можно легко вызвать, если у вас есть одна операция. Что вы можете сделать, так это принять свою реализацию, чтобы составить список. Недостатком является то, что вы должны спроектировать свой распознаватель таким образом, чтобы обрабатывать список входных данных. Конечно, иногда это может оказаться сложной задачей, но вы можете воспользоваться преимуществами массовых вставок, например, в SQL.
Вы также можете просто отправить 480 http-запросов, как в REST. Это также может быть нормально, или вы отправляете партии по 3 (или десять, или 20):
fragment Response on ShopCreditTransaction {
id,
apiTransactionId,
shopStatus,
md5Hash
}
mutation batch(
$first: CreateShopCreditTransactionInput,
$second: CreateShopCreditTransactionInput,
$third: CreateShopCreditTransactionInput
) {
first: createShopCreditTransaction(createShopCreditTransactionInput: $first) {
...Response
}
second: createShopCreditTransaction(createShopCreditTransactionInput: $second) {
...Response
}
third: createShopCreditTransaction(createShopCreditTransactionInput: $third) {
...Response
}
}
Но здесь все эти вставки также выполняются последовательно, поэтому от этого подхода мало что можно получить с точки зрения производительности или используемых ресурсов.
Комментарии:
1. Это очень интересно. Хорошо, в будущем я буду искать все типы мутаций на стороне сервера. Я попробую сегодня, может быть, это сработает. 🙂 Да, «пакет» на стороне клиента-это всего лишь список синглов, также выполняемых последовательно на стороне сервера. Это не то, что мне нужно. 😉 Но я буду искать свои модели на стороне сервера и типы мутаций. Большое спасибо! 🙂