Я запускаю приведенный ниже код, но при его запуске получаю исключение YogabandyFee
.
var payoutResult = await _dbContext.StripePayouts
.Where(p => p.StripePayoutId == stripePayoutId)
.Select(p => new { p.GrossAmount, p.TotalTokens, p.NetAmount, p.ReconciledTransfers, p.EditedDate, p.YogabandyFee })
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.GrossAmount, grossAmount)
.SetProperty(p => p.TotalTokens, totalTokens)
.SetProperty(p => p.YogabandyFee, p => new YogabandyFee { TotalFee = (grossAmount - p.NetAmount - platformFee) } )
.SetProperty(p => p.ReconciledTransfers, true)
.SetProperty(p => p.EditedDate, DateTime.UtcNow)
);
Можно ли это сделать? Могу ли я создать новую запись в этом типе обновлений?
Вот ошибка:
System.InvalidOperationException: выражение LINQ 'DbSet().Where(s => s.StripePayoutId == __stripePayoutId_0).LeftJoin(inner: DbSet(), externalKeySelector: s => EF.Property(s, "Id"),innerKeySelector : y => EF.Property(y, "StripePayoutId"), resultSelector: (o, i) => new TransparentIdentifier (Outer = o, Inner = i)).Select(s => new { GrossAmount = s.Outer. GrossAmount, TotalTokens = s.Outer.TotalTokens, NetAmount = s.Outer.NetAmount, ReconciledTransfers = s.Outer.ReconciledTransfers, EditedDate = s.Outer.EditedDate, YogabandyFee = s.Inner }) .ExecuteUpdate(setters => setters.SetProperty ( propertyExpression: p => p.GrossAmount, valueExpression: __grossAmount_1).SetProperty( propertyExpression: p => p.TotalTokens, valueExpression: __totalTokens_2).SetProperty( propertyExpression: p => p.ReconciledTransfers, valueExpression: True).SetProperty( propertyExpression : p => p.EditedDate, valueExpression: DateTime.UtcNow).SetProperty(propertyExpression: p => p.YogabandyFee, valueExpression: p => new YogabandyFee { TotalFee = __grossAmount_1 - p.NetAmount - __platformFee_3 } ))' не может быть переведено. Дополнительная информация: Следующий лямбда-аргумент SetProperty не представляет собой допустимое свойство, которое нужно установить: «p => p.YogabandyFee». См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации. в Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression MethodCallExpression) в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(выражение выражения) в Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.Translate(Ex) выражение сжатия) в Microsoft.EntityFrameworkCore.Query.QueryCompilationContext .CreateQueryExecutor[TResult](запрос-выражение) в Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](запрос-выражение, логическая асинхронность) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](база данных IDatabase, запрос-выражение) , модель IModel, логическая асинхронность) в компиляторе Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0
1.b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func
1) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Запрос выражения, CancellationToken cancelToken) в Microsoft. EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](выражение, CancellationToken cancelToken) в Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.ExecuteUpdateAsync[TSource](IQueryable1 source, Expression
1 setPropertyCalls, CancellationToken cancelToken) в Functions.ReconcileTransfers.Run(String myQueue) пункт) в /Users/charles/yogabandy2020/Functions/ReconcileTransfers.cs:line 119
Когда я обновляю объект StripePayout
, я также хочу создать новый дочерний объект YogabandyFee
, но не думаю, что это возможно, поэтому я соответствующим образом скорректировал свой код.
Похоже, вы пытаетесь обновить только один StripePayout. Даже если вы хотите добавить к нему дочерний объект, это все равно тот же объект. Пробовали ли вы получить объект из платформы сущностей, обновить объект в С#, а затем использовать метод UpdateAsync + SaveAsync в структуре сущностей?
var payoutResult = await _dbContext.StripePayouts .FirstOrDefault(p => p.StripePayoutId == stripePayoutId);
payoutResult.Yogabandy = new YogabandyFee { TotalFee = (grossAmount - p.NetAmount - platformFee);
await _dbContext.UpdateAsyc(payoutResult);
Возможно, после этого вам также понадобится сделать await _dbContext.SaveAsync(payoutResult);
Я только что проверил это, поэтому добавил это в конец моего первоначального ответа с примером
Исключение System.InvalidOperationException выдается, поскольку метод ExecuteUpdateAsync в EF не поддерживает создание или назначение новых сущностей непосредственно в операции обновления. Поэтому вы можете сначала обновить основную сущность, а затем обновить YogabandyFee.
Есть ли способ сделать это за один звонок? Потому что, если я обновлю основной объект с помощью ExecuteUpdateAsync, я не получу идентификатор обратно, тогда мне придется получить идентификатор или не использовать ExecuteUpdateAsync. Тогда мне пришлось бы сделать еще один звонок, чтобы обновиться YogabandyFee
Я просто стараюсь свести количество звонков к минимуму.
ExecuteUpdateAsync попытается создать SQL-запрос на основе написанного вами кода C#. В коде С# вы пытаетесь обновить две таблицы одновременно в одном запросе. Это невозможно. Даже при написании собственного SQL большинство DMBS не позволяют этого. (за исключением MySQL)
Из вашего сообщения об ошибке
Следующий лямбда-аргумент SetProperty не представляет собой допустимое свойство, которое нужно установить: «p => p.YogabandyFee»
похоже, что парсер ищет свойство, а не объект, поэтому я попытался установить TotalFee
из YogabandyFee
:
var payoutResult = await _dbContext.StripePayouts
.Where(p => p.StripePayoutId == stripePayoutId)
.Select(p => new { p.GrossAmount, p.TotalTokens, p.NetAmount, p.ReconciledTransfers, p.EditedDate, p.YogabandyFee })
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.GrossAmount, grossAmount)
.SetProperty(p => p.TotalTokens, totalTokens)
.SetProperty(p => p.YogabandyFee.TotalFee, p => grossAmount - p.NetAmount - platformFee )
.SetProperty(p => p.ReconciledTransfers, true)
.SetProperty(p => p.EditedDate, DateTime.UtcNow)
);
И вы увидите эту ошибку:
Множественные вызовы SetProperty относятся к разным таблицам («p => p.YogabandyFee.TotalFee» и «p => p.EditedDate»). Один вызов «ExecuteUpdate» может обновить столбцы только одной таблицы.
Я пытаюсь сказать: ExecuteUpdate
можно обновить только одну таблицу. Думаю, ответ на вопрос «Могу ли я создать новую запись в этом типе обновлений?» нет.
Мои предложения:
UnitOfWork
— это позволит вам выполнять несколько запросов _dbContext
в одной транзакции.Вы также можете сделать два отдельных ExecuteUpdateAsync
, по одному для каждого обновления таблицы, если это не проблема с производительностью.
Если вы обновляете только один объект StripePayout, и он небольшой, я думаю, что использование SaveChanges() подойдет больше.
class Program
{
static async Task Main(string[] args)
{
// await FillDatabase();
await UpdateStripePayout(1, 1000m, 50, 50m);
}
private static async Task FillDatabase()
{
using var _dbContext = new AppDbContext();
await _dbContext.StripePayouts.AddAsync(new StripePayout()
{
EditedDate = new DateTime(),
GrossAmount = new decimal(123.4),
NetAmount = new decimal(123.4),
ReconciledTransfers = false,
StripePayoutId = 1,
TotalTokens = 10,
YogabandyFee = new YogabandyFee()
});
await _dbContext.SaveChangesAsync();
}
public static async Task UpdateStripePayout(int stripePayoutId, decimal grossAmount, int totalTokens, decimal platformFee)
{
using var _dbContext = new AppDbContext();
var payoutResult = await _dbContext.StripePayouts
.FirstOrDefaultAsync(p => p.StripePayoutId == stripePayoutId);
if (payoutResult == null)
{
return; // You can raise an error here if you like
}
payoutResult.GrossAmount = grossAmount;
payoutResult.TotalTokens = totalTokens;
payoutResult.ReconciledTransfers = true;
payoutResult.EditedDate = DateTime.UtcNow;
payoutResult.YogabandyFee = new YogabandyFee()
{ TotalFee = (grossAmount - payoutResult.NetAmount - platformFee) };
_dbContext.Update(payoutResult);
await _dbContext.SaveChangesAsync();
var newResult = await _dbContext.StripePayouts.FirstAsync(p => p.StripePayoutId == stripePayoutId);
Console.WriteLine(newResult.YogabandyFee.TotalFee);
}
}
Я также нашел это в документации Microsoft: https://learn.microsoft.com/en-us/ef/core/saving/
Если вам нужно манипулировать графом объектов (т. е. несколькими взаимосвязанными объектами), используйте SaveChanges; он выяснит правильный порядок изменений и то, как связать все вместе. «Я хочу обновить блог, изменив некоторые его публикации и удалив другие»
ок, спасибо за помощь.
ваш вариант использования для обновления одного объекта? Или вы пытаетесь обновить несколько объектов.