MassTransitStateMachine schedules broken? - c#

Using Azure Service Bus as transport, but scheduled messages not working other than when calling from inside an IConsumer.
I spent hours and days and still have little idea what is going on.
Can someone explain what I need to do to get schedules working from the state machine using azure service bus? And perhaps why schedule message works from IConsumer context but not anywhere else.
public class BatchCollector : MassTransitStateMachine<BufferSaga>
{
public BatchCollector(IBatchFactory batchFactory)
{
InstanceState(saga => saga.State);
Event(() => BufferedCommandDetected,
_ => _.CorrelateById(context => context.Message.GetBatchId()));
Schedule(() => WindowElapsed, x => x.BatchCompletionId, x =>
{
x.Delay = TimeSpan.FromSeconds(5);
x.Received = e => e.CorrelateById(context => context.Message.CorrelationId);
});
Initially(
When(BufferedCommandDetected)
.Then(
context =>
{
context.Instance.CorrelationId = context.Data.GetBatchId();
context.Instance.Id = Guid.NewGuid().ToString("N");
context.Instance.Buffer.Add(context.Data);
context.Instance.BatchStartTime = DateTimeOffset.Now;
context.Instance.AbsoluteDeadLine = DateTimeOffset.Now + context.Data.AbsoluteWindowSpan;
context.Instance.SlidingDeadLine = DateTimeOffset.Now + context.Data.SlidingWindowSpan;
})
.Schedule(WindowElapsed,
context => new WindowElapsed {CorrelationId = context.Instance.CorrelationId },
delayProvider: scheduleDelayProvider => scheduleDelayProvider.Data.SlidingWindowSpan < scheduleDelayProvider.Data.AbsoluteWindowSpan ? scheduleDelayProvider.Data.SlidingWindowSpan : scheduleDelayProvider.Data.AbsoluteWindowSpan)
.TransitionTo(Waiting));
During(Waiting,
When(BufferedCommandDetected)
.Then(context =>
{
context.Instance.SlidingDeadLine += context.Data.SlidingWindowSpan;
context.Instance.Buffer.Add(context.Data);
}),
When(WindowElapsed.Received, context => context.Instance.SlidingDeadLine > DateTimeOffset.Now && context.Instance.AbsoluteDeadLine > DateTimeOffset.Now)
.Schedule(WindowElapsed, context => new WindowElapsed { CorrelationId = context.Instance.CorrelationId }),
When(WindowElapsed.Received, context => context.Instance.SlidingDeadLine <= DateTimeOffset.Now || context.Instance.AbsoluteDeadLine <= DateTimeOffset.Now)
//.Unschedule(WindowElapsed)
.Publish(context => new Batch()
{
BatchId = context.Instance.BatchCompletionId ?? Guid.NewGuid(),
Content = context.Instance.Buffer,
StartTime = context.Instance.BatchStartTime,
EndTime = DateTimeOffset.Now
})
.Finalize()
.TransitionTo(BufferCompleted));
SetCompletedWhenFinalized();
}
public Event<BufferedCommand> BufferedCommandDetected { get; private set; }
public Schedule<BufferSaga, WindowElapsed> WindowElapsed { get; private set; }
public State Waiting { get; private set; }
public State BufferCompleted { get; private set; }
}
The bus init:
container.RegisterType<IBusControl>(
new HierarchicalLifetimeManager(),
new InjectionFactory(c =>
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(
cfg =>
{
var azSbHost = cfg.Host(new Uri(CloudConfigurationManager.GetSetting("ServiceBus.Url"))
, host =>
{
host.TokenProvider = TokenProvider
.CreateSharedAccessSignatureTokenProvider
(CloudConfigurationManager.GetSetting("ServiceBus.SharedAccessKeyName"),
CloudConfigurationManager.GetSetting("ServiceBus.AccessKey"),
TokenScope.Namespace);
});
cfg.ReceiveEndpoint(
azSbHost,
"Quartz.Scheduler",
sbConfig =>
{
cfg.UseMessageScheduler(sbConfig.InputAddress);
sbConfig.Consumer(() => new ScheduleMessageConsumer(c.Resolve<IScheduler>()));
}
);
cfg.ReceiveEndpoint(
azSbHost,
Assembly.GetExecutingAssembly().GetName().Name,
sbConfig =>
{
AllClasses.FromAssembliesInBasePath()
.Where(
#class =>
(#class?.Namespace?.StartsWith("bcn",
StringComparison.OrdinalIgnoreCase) ?? false)
&&
#class.GetParentClasses()
.Any(
parent =>
parent.Name.StartsWith("MassTransitStateMachine`1")))
.ForEach(#class =>
{
//dynamic cast to avoid having to deal with generic typing when type is not known until runtime.
dynamic stateMachineExtension =
new DynamicStaticWrapper(typeof(StateMachineSubscriptionExtensions));
stateMachineExtension
.StateMachineSaga(
sbConfig,
c.Resolve(#class),
c.Resolve(typeof(ISagaRepository<>).MakeGenericType(
#class.GetParentClasses().First(parent =>
parent.Name.StartsWith("MassTransitStateMachine`1"))
.GetGenericArguments().First())));
});
AllClasses.FromAssembliesInBasePath()
.Where(
#class =>
(#class?.Namespace?.StartsWith("bcn", StringComparison.OrdinalIgnoreCase) ??
false)
&& #class.GetInterfaces().Any(
#interface =>
#interface?.FullName?.StartsWith("MassTransit.IConsumer`1") ??
false))
.ForEach(#class =>
{
var factoryType = typeof(UnityConsumerFactory<>).MakeGenericType(#class);
//Automatically register consumers.
dynamic consumerFactory = Activator.CreateInstance(
factoryType,
container);
var consumingMethod = typeof(ConsumerExtensions).
GetMethods()
.First(
m =>
m.Name == "Consumer" && m.IsGenericMethod &&
m.GetGenericArguments().Length == 1 &&
m.GetParameters().Length == 3)
.MakeGenericMethod(#class)
.Invoke(null, new object[] {sbConfig, consumerFactory, null});
//Automatically detect which payload contains message data. This message data is stored in blob.
#class.GetInterfaces().Where(
#interface =>
#interface.FullName.StartsWith("MassTransit.IConsumer`1"))
.Select(#interface => #interface.GetGenericArguments().First())
.Where(payload => payload.GetProperties()
.Any(prop => prop.PropertyType.Name.StartsWith("MessageData`1")))
.ForEach(
BlobType =>
typeof(MessageDataConfiguratorExtensions)
.GetMethods()
.First(
method =>
method.GetParameters().First().ParameterType ==
typeof(IConsumePipeConfigurator)
&&
method.GetParameters().Last().ParameterType ==
typeof(IMessageDataRepository))
.MakeGenericMethod(BlobType)
.Invoke(null,
new object[]
{sbConfig, c.Resolve<IMessageDataRepository>()}));
});
});
cfg.UseServiceBusMessageScheduler();
//azSbHost.
});
return bus;
}));
container.RegisterType<IBus, IBusControl>();
container.RegisterType<IBus, IBusControl>(new ContainerControlledLifetimeManager());
And then started:
var container = UnityConfig.GetConfiguredContainer();
var bus = container.Resolve<IBusControl>();
bus.Start();
var scheduler = container.Resolve<IScheduler>();
scheduler.Start();
bus.Publish<BufferedCommand>(new BufferedCommandAdapter<decimal>(10m, TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(5)));

Are you setting up the job factory for quartz? Take a look at how the QuartzIntegration library does the setup:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.QuartzIntegration/QuartzIntegrationExtensions.cs
Also, use the observer around the bus so that quartz is started/paused/stopped inline with the bus.
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.QuartzIntegration/Configuration/SchedulerBusObserver.cs

Related

How can we stream through multiple files

In my case after checking one XML file the method will go in to if or else condition where it will check another XML file
so How we can stream through multiple files? below is the code of class file.
public override void Execute(IParserContext context)
{
_context = context;
foreach (var file in context.Files)
{
try
{
IParserFile childFile;
String clusterName;
Cluster cluster = null;
List<string> devices = GetDeviceNames(file);
foreach (string device in devices)
{
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSplitterStates", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
var isRp4vm = CheckIfRP4VM(childFile);
if (!isRp4vm.HasValue)
{
file.Log.Message((int)ErrorCodes.FailClusterIdentification);
continue;
}
if (isRp4vm.HasValue && isRp4vm.Value == true)
{
clusterName = "RP4VM";
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemSettings", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
cluster = ParseSystemSettingsByDevice(childFile, device);
if (cluster == null)
{
file.Log.Message((int)ErrorCodes.FailClusterName);
continue;
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetMonitoredParameters", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseMonitoredParameters(childFile);
}
else
{
childFile = file.Children.Where(x => x.FileName.StartsWith("GetLocalCluster", StringComparison.InvariantCultureIgnoreCase)
&& x.FullPath.Contains("RECOVERPOINT\\" + device, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
clusterName = ParseLocalCluster(childFile);
if (clusterName == null)
{
file.Log.Message((int)ErrorCodes.FailClusterName);
continue; //Skip the device of we can't get the cluster name.
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemSettings", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
cluster = ParseSystemSettingsByClusterName(childFile, clusterName);
if (cluster == null)
{
file.Log.Message((int)ErrorCodes.FailClusterParsing);
continue; //Skip the device of we can't get the cluster info.
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetGroupVolumes", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseGroupVolumes(childFile, cluster);
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSANVolumes", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseSANVolumes(childFile, cluster);
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemReport", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseSystemReport(childFile, cluster);
}
}
catch (Exception ex)
{
file.Log.Message((int)ErrorCodes.ExecuteErrorMessage);
file.Log.Error(file.FileName, ex);
}
}
}
i am able to execute only one method
CheckIfRP4VM. after this method i want to go in to any of the condition to check other files.
how we can achieve that? below is my test class
[Test]
public void Processfile_GetSplitterStatesXML()
{
Mock<IParserContext> mockIParserContext = new Mock<IParserContext>();
Mock<IParserFile> mockIParserChildFile = new Mock<IParserFile>();
Mock<IDictionary<string, string>> mockIDictionary = new Mock<IDictionary<string, string>>();
Mock<IParserLog> mockIParserChildLog = new Mock<IParserLog>();
TestUtility utility = new TestUtility();
string serverFileName = "GetSplitterStates";
string serverFileExtension = ".xml";
string serverFileNameWithExtension = serverFileName + serverFileExtension;
string relativeFilePath = #"\SampleTestFiles\XML\RecoverPointParser\STORAGE\RECOVERPOINT\splitters\" + serverFileNameWithExtension;
string absoluteFilePath = utility.getExecutionAssemblyLocation() + relativeFilePath;
Stream stream = utility.getFileStream(relativeFilePath);
IList<IParserFile> parserFiles = new List<IParserFile>() { };
mockIParserChildFile.Setup(x => x.Guid).Returns(new Guid());
mockIParserChildFile.Setup(x => x.FullPath).Returns(absoluteFilePath);
mockIParserChildFile.Setup(x => x.FileName).Returns(serverFileNameWithExtension);
mockIParserChildFile.Setup(x => x.Extension).Returns(serverFileExtension);
mockIParserChildFile.Setup(x => x.SizeInBytes).Returns(2);
mockIParserChildFile.Setup(x => x.Name).Returns(serverFileName);
mockIParserChildFile.Setup(x => x.ModifiedDateTime).Returns(DateTime.Now);
mockIParserChildFile.Setup(x => x.Children).Returns(parserFiles);
mockIParserChildFile.Setup(x => x.Properties).Returns(mockIDictionary.Object);
mockIParserChildFile.Setup(x => x.Log).Returns(mockIParserChildLog.Object);
mockIParserChildFile.Setup(x => x.RequestFileId).Returns(1);
mockIParserChildFile.Setup(x => x.Open()).Returns(stream);
parserFiles.Add(mockIParserChildFile.Object);
mockIParserContext.Setup(x => x.Files).Returns(parserFiles);
RecoverPointParser RecoverPointParser = new RecoverPointParser();
RecoverPointParser.Execute(mockIParserContext.Object);
mockIParserChildLog.Verify(x => x.Error(It.IsAny<string>(), It.IsAny<Exception>()), Times.AtLeastOnce);
}
so far i am able to check with one file only. as it has if condition i am unable to do it.

FakeItEasy: How check if param is changed when method is calling?

I have following service method
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, string schemaIdOrName, Q q)
{
Guard.NotNull(context, nameof(context));
if (q == null)
{
return EmptyContents;
}
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
var permissionReadOwn = Permissions.ForApp(Permissions.AppContentReadOwn, context.App.Name, schemaIdOrName);
if (context.Permissions.Allows(permissionReadOwn))
{
q.CreatedBy = context.User.Token();
}
using (Profiler.TraceMethod<ContentQueryService>())
{
q = await queryParser.ParseAsync(context, q, schema);
var contents = await contentRepository.QueryAsync(context.App, schema, q, context.Scope());
if (q.Ids != null && q.Ids.Count > 0)
{
contents = contents.SortSet(x => x.Id, q.Ids);
}
return await TransformAsync(context, contents);
}
}
q.CreatedBy must set value if permission is correct.
How can I test if q.CreatedBy is not empty and has correct value.
I implemented following test, but no idea how check this params?
public async Task QueryAll_should_return_own_user_contents(int isFrontend, int unpublished, SearchScope scope)
{
var ctx = CreateContextWithOwnReadPermission(isFrontend: isFrontend == 1, allowSchema: true)
.WithUnpublished(unpublished == 1);
var content = CreateContent(contentId);
var q = Q.Empty.WithReference(DomainId.NewGuid());
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, q, scope))
.Returns(ResultList.CreateFrom(5, content));
//A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy == null), scope))
// .MustHaveHappened();
//A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy == ctx.User.Token()), scope))
// .MustHaveHappened();
//q.CreatedBy = ctx.User.Token();
var result = await sut.QueryAsync(ctx, schemaId.Name, q);
Assert.Equal(contentData, result[0].Data);
Assert.Equal(contentId, result[0].Id);
Assert.Equal(5, result.Total);
}

EF Core. Don't know how to handle concurrency conflicts

I've been trying to figure out why I can't add new records but I can update them properly. I am having no luck and have looked into this issue on other topics so I am asking specifically for my situation.
EntityConfig and Methods
public void Configure(EntityTypeBuilder<Story> builder)
{
builder.ToTable("Stories");
builder.HasKey(x => x.Id);
builder.HasOne(s => s.Author)
.WithMany(a => a.Stories)
.HasForeignKey(s => s.AuthorId);
builder.OwnsMany(x => x.Ratings, x =>
{
x.WithOwner().HasForeignKey(x => x.StoryId);
x.Property(x => x.Score);
x.HasKey(y => new { y.StoryId, y.UserId });
x.ToTable("Ratings");
});
builder.OwnsMany(x => x.Favorites, x =>
{
x.WithOwner().HasForeignKey(x => x.StoryId);
x.HasKey(y => new { y.StoryId, y.UserId });
x.ToTable("Favorites");
});
}
public void WriteRating(int userId, double newRating)
{
if (HasUserWrittenRating(userId))
{
Ratings.FirstOrDefault(c => c.UserId == userId && c.StoryId == this.Id).Score = newRating;
AverageRating = Ratings.Average(r => r.Score);
}
else
{
Ratings.Add(new Rating() { StoryId = this.Id, Score = newRating, UserId = userId });
AverageRating = ((AverageRating * NumberOfRatings - 1) + newRating) / NumberOfRatings;
}
}
private bool HasUserWrittenRating(int userId)
{
return Ratings.Any(rat => rat.UserId == userId);
}
public async Task WriteRatingForStory(int storyId, int userId, int score)
{
// Get the story that was rated
var story = await _dbContext.Stories.FirstOrDefaultAsync(story => story.Id == storyId);
// Updating thestory with the new rating
story.WriteRating(userId, score);
//_dbContext.Stories.Update(story);
// Save the updated story to the database
var saved = false;
while (!saved)
{
try
{
// Attempt to save changes to the database
_dbContext.SaveChanges();
saved = true;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
if (entry.Entity is Story)
{
var proposedValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
foreach (var property in proposedValues.Properties)
{
var proposedValue = proposedValues[property];
var databaseValue = databaseValues[property];
// TODO: decide which value should be written to database
// proposedValues[property] = <value to be saved>;
}
// Refresh original values to bypass next concurrency check
entry.OriginalValues.SetValues(databaseValues);
}
else
{
throw new NotSupportedException(
"Don't know how to handle concurrency conflicts for "
+ entry.Metadata.Name);
}
}
}
}
}
Ratings DB Structure (UserId, StoryId are both Primary, Score as
float)
Stories DB Structure (Id as Primary, AuthorId, Title, Date, Score,
AverageRating, Url)

Run a controller as background worker

I am trying to convert a controller to async operation or run it as a background worker.
Solution 1:
I tried below method but it give me an error saying DBContext has been disposed.
public IHttpActionResult ChargebackAllOpenAR(int clientId)
{
HostingEnvironment.QueueBackgroundWorkItem(clt => _clientService.ChargebackAllOpenAR(clientId));
//_clientService.ChargebackAllOpenAR(clientId);
return Ok();
}
Solution 2: But it runs as a normal synchronous process
[HttpPost]
public async Task<IHttpActionResult> ChargebackAllOpenAR(int clientId)
{
await Task.Run(() => _clientService.ChargebackAllOpenAR(clientId));
//_clientService.ChargebackAllOpenAR(clientId);
return Ok();
}
Solution 3: I tried this but give same error DBContext has been disposed.
https://github.com/StephenCleary/AspNetBackgroundTasks
Main Code:
public class ClientService : IClientService
{
private IRepository<Client> _clientRepository;
private IRepository<Invoice> _invoiceRepository;
private IRepository<Advance> _advanceRepository;
private IRepository<FinancialAccount> _financialAccountRepository;
private IClientTableRepository _clientTableRepository;
private IRepository<Transaction> _transactionRepository;
private IRepository<AppCredit> _appCreditRepository;
private IInvoiceService _invoiceService;
private IUserProfileRepository _userProfileRepository;
private IRepository<Domain.Payment> _paymentRepository;
private ARUnitOfWork _unitOfWork;
public ClientService(IRepository<Client> clientRepository,
IRepository<Invoice> invoiceRepository,
IRepository<Advance> advanceRepository,
IRepository<FinancialAccount> financialAccountRepository,
IClientTableRepository clientTableRepository,
IRepository<Transaction> transactionRepository,
IRepository<AppCredit> appCreditRepository,
IInvoiceService invoiceService,
IUserProfileRepository userProfileRepository,
IRepository<Domain.Payment> paymentRepository,
ARUnitOfWork unitOfWork)
{
_clientRepository = clientRepository;
_invoiceRepository = invoiceRepository;
_advanceRepository = advanceRepository;
_financialAccountRepository = financialAccountRepository;
_clientTableRepository = clientTableRepository;
_transactionRepository = transactionRepository;
_appCreditRepository = appCreditRepository;
_invoiceService = invoiceService;
_userProfileRepository = userProfileRepository;
_paymentRepository = paymentRepository;
_unitOfWork = unitOfWork;
}
public void ChargebackAllOpenAR(int clientId)
{
var client = _clientRepository.Find(c => c.Id == clientId, i => i.FinancialAccounts).First();
var transaction = new Transaction(clientId, TransactionType.Buyout);
ChargebackInvoices(client, transaction);
ChargebackOpenCredits(client, transaction);
ChargebackAdvances(client, transaction);
transaction.LineItems = transaction.LineItems.Where(li => li != null).ToList();
_transactionRepository.Insert(transaction);
_unitOfWork.SaveChanges();
}
private void ChargebackInvoices(Client client, Transaction transaction)
{
var openARAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.OpenAR);
var escrowReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.EscrowReserve);
var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
var factoringFeeRevenueAccount = _financialAccountRepository.GetSingle(fa => fa.Division_Id == openARAccount.Division_Id && fa.AccountType == (int)SystemAccountType.FactoringFeeRevenue);
IEnumerable<Invoice> invoices = _invoiceRepository.Find(i => i.Client_Id == client.Id
&& i.Asset_Type == AssetType.Invoice
&& i.IsDeleted == false
&& i.Status == InvoiceStatus.Purchased,
i => i.InvoicePurchase.Terms.CollectionsFees,
i => i.TransactionLineItems)
.Where(i => i.AmountOutstanding.Value != 0)
.ToList();
foreach (Invoice invoice in invoices)
{
invoice.StatusReason = InvoiceStatusReason.Buyout;
invoice.DateClosed = DateUtil.GetSystemNow();
//Credit Open A/R for amount outstanding
transaction.LineItems.Add(openARAccount.CreateTransaction(invoice.AmountOutstanding, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, invoice));
//Credit Fee Revenue for collections fees
CollectionsFeeCharge collectionsFee = _invoiceService.CalculateCollectionsFeeForInvoiceBuyout(invoice);
transaction.LineItems.Add(factoringFeeRevenueAccount.CreateTransaction(collectionsFee.Amount, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, invoice));
//Debit Escrow Reserves for remaining invoice escrow
IEnumerable<TransactionLineItem> transactionLineItems = invoice.TransactionLineItems.Where(tli => tli.FinancialAccount.AccountType == (int)ClientAccountType.EscrowReserve);
var escrowAmount = transactionLineItems.Sum(tli => tli.AccountingTransactionType == AccountingTransactionType.CREDIT ? tli.Amount : -tli.Amount);
transaction.LineItems.Add(escrowReservesAccount.CreateTransaction(escrowAmount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, invoice));
//Debit Cash Reserves for (Open AR - invoice escrow + collections fees)
transaction.LineItems.Add(cashReservesAccount.CreateTransaction(invoice.AmountOutstanding - escrowAmount + collectionsFee.Amount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, invoice));
_invoiceRepository.Update(invoice);
}
}
private void ChargebackOpenCredits(Client client, Transaction transaction)
{
var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
var openCreditsAccount = client.FinancialAccounts.FirstOrDefault(fa => fa.AccountType == ClientAccountType.OpenCredits);
int loggedInUserId = _userProfileRepository.GetLoggedInUserId();
IEnumerable<AppCredit> appCredits = _appCreditRepository.Find(ac => (ac.Status == AppCreditStatus.Posted || ac.Status == AppCreditStatus.Ready)
&& ac.Client_Id == client.Id
&& ac.Type == AppCreditType.OpenCredit);
var ids = appCredits.Select(ac => ac.Payment_Id).Distinct().Where(id => id != null).Cast<int>();
var payments = _paymentRepository.Find(p => ids.Contains(p.Id), p => p.AppCredits)
.ToDictionary(p => p.Id);
foreach (AppCredit appCredit in appCredits)
{
DateTime now = DateUtil.GetSystemNow();
// mark open credit as removed
appCredit.Status = AppCreditStatus.Removed;
appCredit.RemovedBy_Id = loggedInUserId;
appCredit.DateRemoved = now;
// add posted reserve app credit to the payment with same amount
AppCredit reserveAppCredit = new AppCredit()
{
Type = AppCreditType.Reserve,
Status = AppCreditStatus.Posted,
Amount = appCredit.Amount,
Client_Id = appCredit.Client_Id,
Payment_Id = appCredit.Payment_Id,
PostedBy_Id = loggedInUserId,
DatePosted = now
};
Domain.Payment payment = payments[appCredit.Payment_Id];
payment.AppCredits.Add(reserveAppCredit);
if (payment.Status == Payment.Domain.PaymentStatus.Reopened
&& payment.AmountRemaining.IsZero()
&& !payment.AppCredits.Any(ac => ac.Status == AppCreditStatus.Ready))
{
payment.Status = Payment.Domain.PaymentStatus.Posted;
}
// Debit Open Credits
transaction.LineItems.Add(openCreditsAccount.CreateTransaction(appCredit.Amount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout));
// Credit Cash Reserves
transaction.LineItems.Add(cashReservesAccount.CreateTransaction(appCredit.Amount, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout));
payment?.Transactions.Add(transaction);
}
}
private void ChargebackAdvances(Client client, Transaction transaction)
{
var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
var fuelAdvancceARAccount = client.FinancialAccounts.FirstOrDefault(fa => fa.AccountType == ClientAccountType.FuelAdvanceAR);
IEnumerable<Advance> advances = _advanceRepository.Find(a => a.Client_Id == client.Id
&& a.Asset_Type == AssetType.Advance
&& a.IsDeleted == false
&& a.Status == InvoiceStatus.Purchased)
.Where(a => a.AmountOutstanding.Value != 0)
.ToList();
foreach (Advance advance in advances)
{
advance.StatusReason = InvoiceStatusReason.Buyout;
advance.DateClosed = DateUtil.GetSystemNow();
//Debit Cash Reserves
transaction.LineItems.Add(cashReservesAccount.CreateTransaction(advance.AmountOutstanding, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, advance));
//Credit Fuel Advance A/R
transaction.LineItems.Add(fuelAdvancceARAccount.CreateTransaction(advance.AmountOutstanding, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, advance));
}
}
Finally working..
[Route("chargeback-all-open-ar-async")]
[DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
[HttpPost]
public IHttpActionResult ChargebackAllOpenARAsync(int clientId)
{
_clientService.ChargebackAllOpenAR(clientId);
return Ok();
}
[Route("chargeback-all-open-ar")]
[DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
[HttpPost]
public IHttpActionResult ChargebackAllOpenAR(int clientId)
{
HostingEnvironment.QueueBackgroundWorkItem(clt => ChargebackAllOpenARTask(Request, clientId));
return Ok();
}
private async Task ChargebackAllOpenARTask(HttpRequestMessage request, int clientId)
{
//Calls ChargebackAllOpenARAsync method
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = request.Headers.Authorization;
var url = new Uri(request.RequestUri.AbsoluteUri + "-async", UriKind.Absolute);
await client.PostAsync(url, new StringContent(string.Empty));
}
}
[Route("chargeback-all-open-ar")]
[DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
[FireAndForget]
[HttpPost]
public IHttpActionResult ChargebackAllOpenAR(int clientId, [FromBody]bool isFireAndForget)
{
_clientService.ChargebackAllOpenAR(clientId);
return Ok();
}
public class FireAndForgetAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var request = actionContext.Request;
bool isfireAndForget = (bool)actionContext.ActionArguments["isFireAndForget"];
if (isfireAndForget)
{
QueueBackgroundWorker(request);
actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
return;
}
}
private void QueueBackgroundWorker(HttpRequestMessage request)
{
HostingEnvironment.QueueBackgroundWorkItem(clt => GetTask(request));
}
private async Task GetTask(HttpRequestMessage request)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = request.Headers.Authorization;
var url = new Uri(request.RequestUri.AbsoluteUri, UriKind.Absolute);
var param = new Dictionary<string, string>
{
{ "isFireAndForget", "false" }
};
var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(param) };
await client.SendAsync(req);
}
}
}

C# ef second operation started before previous asynchronous operation completed

Just trying to do normal query on ef but getting below error message
"A second operation started on this context before a previous asynchronous operation completed"
I am using await for all aync call but not sure what could be causing this issue.
here' code
var sids = await context.Sites.Where(s => slist.Contains(s.JVSiteID)).ToListAsync();
var header = await context.GenericCsvHeaders.FirstOrDefaultAsync(x => x.Header == csvh) ?? new GenericCsvHeader { Header = csvh };
context.Entry<GenericCsvHeader>(header).State = (header.GenericCsvHeaderID == 0) ? EntityState.Added : EntityState.Unchanged;
var sa = await context.SAs.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.SAName)).Select(x => new { x.SAName, x.SACode, x.SAID }).ToListAsync();
if (sa.Count > 0)
sasitelist = await context.Site_SA.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var az = await context.Azimuths.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.AzimuthName)).Select(x => new { x.AzimuthName, x.AzimuthID }).ToListAsync();
if (az.Count > 0)
azsitelist = await context.Site_Azimuth.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var rows = new List<dynamic>(); //getting this list from csv file via csvHelper
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
UpdateSite(s, r, map);
}
private async void UpdateSite(Site site, dynamic csvSite, IDictionary<string, string> map)
{
context.Entry(site).State = (site.StateID == 0) ? EntityState.Added : EntityState.Modified;
site.SiteForAuditTrail = site;
if (map.ContainsKey("SiteName") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite,"SiteName")))
site.SiteName = GetStringOfDynamic(map,csvSite, "SiteName");
if (map.ContainsKey("State") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite, "State")))
{
//getting exception at below line
var state = (await GetRefTypeList<State>(x => x.StateCode == GetStringOfDynamic(map,csvSite, "State"))) ?? new State { StateCode = GetStringOfDynamic(map,csvSite, "State") };
context.Entry(state).State = (state.StateID == 0) ? EntityState.Added : EntityState.Unchanged;
site.State = state;
state.SiteForAuditTrail = site;
}
}
private async Task<T> GetRefTypeList<T>(Func<T, bool> expression) where T : EntityBase
{
if (!refTypes.ContainsKey(typeof(T).Name))
refTypes[typeof(T).Name] = (await context.Set<T>().Where(x => x.StatusID == Data.Enum.Status.Active).ToListAsync());
return (refTypes[typeof(T).Name] as List<T>)?.FirstOrDefault(expression);
}
private string GetStringOfDynamic(IDictionary<string, string> map,dynamic obj, string propertyName)
{
if (((IDictionary<String, Object>)obj).ContainsKey(map[propertyName]))
return (((IDictionary<String, Object>)obj)[map[propertyName]]??"").ToString();
return "";
}
found problem, was missing await when calling UpdateSite
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
**await** UpdateSite(s, r, map);
}
You are having a race condition here. While you are using await you have other code executing potentially before the variable sids, header, sa and az, have been returned.
You should refactor your code so that the remaining code is not executed until these variables are returned. You can do this by using a task and then using the extension WhenAll which will ensure each await has been completed before proceeding. Here is a link to MS docs on Task and WhenAll implementation

Categories

Resources