public async Task Make_Sure_Getting_Active_Material_ReturnsActiveMaterials()
{
var data = MaterialData.GetFakeMaterialList();
var _logger = new Mock<ILogger<GetAllMaterialQueryHandler>>();
var mockSet = FakeDbSetup.GetMockDbSet<DataAccess.Domain.Materials.Material>(data);
var mockContext = FakeDbSetup.GetMockDbContext();
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
mockSet.Setup(m => m.Include(It.IsAny<String>())).Returns(mockSet.Object);
mockContext.Setup(c => c.Materials).Returns(mockSet.Object);
GetAllMaterialQuery query = new GetAllMaterialQuery();
GetAllMaterialQueryHandler handler = new GetAllMaterialQueryHandler(mockContext.Object, _mapper!.Object, _logger.Object);
// Act
var Materials = await handler.Handle(query, new System.Threading.CancellationToken());
Assert.False(Materials[0].IsDeleted);
}
public async Task<List<MaterialViewModel>> Handle(GetAllMaterialQuery request, CancellationToken cancellationToken)
{
try
{
// get only active materials.
var materials = await _context.Materials
.Where(m => !m.IsDeleted)
.AsNoTracking()
.Include(m => m.Well)
.Include(m => m.Rig)
.Include(m => m.Supplier)
.Include(m => m.Chemical!.MaterialType)
.Include(m => m.Chemical!.MaterialSubType)
.Include(m => m.CisProMaterial)
.ToListAsync(cancellationToken);
// on this line I'm getting null
var materialViewModel = _mapper.Map<List<DataAccess.Domain.Materials.Material>, List<MaterialViewModel>>(materials);
return materialViewModel.OrderByDescending(x => x.ReceivedOn)
.ThenBy(x => x.Name)
.ThenBy(x => x.SpecificGravity).ToList();
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
throw;
}
// returns null
var materialViewModel = _mapper.Map<List<DataAccess.Domain.Materials.Material>, List<MaterialViewModel>>(materials);
This works fine while running actual application, while it returns null in unit testing.I have added the necessary mappings as well
Related
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);
}
I have the following working code:
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
The Validate method returns a ValidationResult object.
I want to change the Validate method to ValidateAsync, then I use async/await inside the Select. Now it does not return IEnumerable<ValidationResult>, but instead IEnumerable<Task<ValidationResult>> and the SelectMany call does not work anymore.
So I changed the code like this:
var failures = _validators
.Select(async v => await v.ValidateAsync(request))
.SelectMany(result => result.Result.Errors)
.Where(f => f != null)
.ToList();
I'm using the Result property of the Task object to access the ValidationResult object.
I remember reading something a long time ago about using that Task.Result could lead to possible deadlocks. Is this solution good or is there a better one?
I use this extension:
public static async Task<TResult[]> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (selector == null)
{
throw new ArgumentNullException(nameof(selector));
}
return await Task.WhenAll(source.Select(selector));
}
var temp = await _validators
.SelectAsync(v => v.ValidateAsync(request));
var result = temp.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
Message:
"System.NotSupportedException was unhandled
Message: An unhandled exception of type 'System.NotSupportedException' occurred in mscorlib.dll
Additional information: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe."
Code:
public async Task<IEnumerable<UserLangDTO>> ImportLang(int userId)
{
var userLangs = new List<UserLangDTO>();
using (FirstContext ctx = new FirstContext())
{
if (await (ctx.UserLang.AnyAsync(u => u.UserId == userId)) == false)
//some exception here
userLangs = await ctx.UserLang.AsNoTracking()
.Where(ul => ul.UserId == userId)
.Join(ctx.Language,
u => u.LangID,
l => l.LangID,
(u, l) => new { u, l })
.Join(ctx.Level,
ul => ul.u.LevelID,
le => le.LevelID,
(ul, le) => new { ul, le })
.Select(r => new UserLangDTO
{
UserId = r.ul.u.UserId,
Language = r.ul.l.Language,
Level = r.le.Level,
}).ToListAsync().ConfigureAwait(false);
}
using (SecondContext ctx = new SecondContext())
{
if ( await (ctx.UserLangs.AnyAsync(u => u.UserId == userId)) == true && userLangs.Any())
ctx.UserLangs.RemoveRange(ctx.UserLangs.Where(u => u.UserId == userId));
if (await hasUserLangs && userLangs.Any())
{
userLangs.ForEach(async l =>
{
var userLanguage = new UserLang();
userLanguage.UserId = userId;
userLanguage.LanguageId = await ctx.Languages.AsNoTracking()
.Where(la => la.NameEn == l.Language)
.Select(la => la.Id).FirstOrDefaultAsync().ConfigureAwait(false);
userLanguage.LevelId = await ctx.Levels.AsNoTracking()
.Where(la => la.NameEn == l.Language)
.Select(la => la.Id).FirstOrDefaultAsync().ConfigureAwait(false);
ctx.UserLangs.Add(userLanguage);
});
}
await ctx.SaveChangesAsync().ConfigureAwait(false);
}
return userLangs;
}
What I Tried:
I'm not sure what I'm doing wrong, I tried different stuff like :
1.
await Task.Run(() => Parallel.ForEach(strings, s =>
{
DoSomething(s);
}));
2.
var tasks = userLangs.Select(async l =>
{
//rest of the code here
}
await Task.WhenAll(tasks);
3.
var tasks = userLangs.Select(async l =>
{
//rest of the code here
}
await Task.WhenAll(tasks);
await ctx.SaveChangesAsync().ConfigureAwait(false);
Other trial and error attempts, that i do not rember now
What am I doing wrong?
Here's your problem:
userLangs.ForEach(async
This is creating an async void method, because ForEach does not understand asynchronous delegates. So the body of the ForEach will be run concurrently, and Entity Framework does not support concurrent asynchronous access.
Change the ForEach to a foreach, and you should be good:
foreach (var l in userLangs)
{
var userLanguage = new UserLang();
userLanguage.UserId = userId;
userLanguage.LanguageId = await ...
}
For more information, see the "avoid async void" guidance in my Async Best Practices article.
For those who have the ability to use .NET 6, there is now a Parallel.ForEachAsync()
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreachasync?view=net-6.0
https://www.hanselman.com/blog/parallelforeachasync-in-net-6
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
I have an input stream from an RS-232 port and a queue of commands to the serial port using Rx streams. I have simplified my code as follows:
void Init()
{
SerialPort srl;
... // open serial port
IObservable<string> obInput =
Observable.FromEventPattern<
SerialDataReceivedEventHandler,
SerialDataReceivedEventArgs>
(
handler => srl.DataReceived += handler,
handler => srl.DataReceived -= handler
).SelectMany(_ =>
{
List<string> ret;
... //extract messages
return ret;
}).Publish().Refcount();
obCommandOk =
obInput
.Where(msg => msg == "OK" || msg == "KO");
var sAction = new Subject<string>();
var sCommandOk = new Subject<Tuple<string,bool>>();
sAction
.Do(srl.WriteLine)
.Zip(obCommandOk, (cmd, result) =>
{
if (result == "OK")
sCommandOk.OnNext(Tuple.Create(cmd, true))
else
sCommandOk.OnNext(Tuple.Create(cmd, false))
});
}
async bool Command(string cmd)
{
sAction.OnNext(cmd);
return
await sCommandOk
.Where(t => t.Item1 == cmd)
.Select(t => t.Item2)
.FirstAsync();
}
Sometimes happens that after OnNext the result has been already pushed to sCommandOk and so I lose it.
Can you suggest me a better approach to avoid losing responses?
You have a race condition in your Command method.
You push the Action, then subscribe to the result.
If the result is quick, then you loose it.
a small change here can mitigate that:
async Task<bool> Command(string cmd)
{
var result = sCommandOk
.Where(t => t.Item1 == cmd)
.Select(t => t.Item2)
.FirstAsync();
sAction.OnNext(cmd);
return await result;
}
I think you can further optimise by removing the sCommandOk subject all together
async Task<bool> Command(string cmd)
{
var result = obInput
.Where(t => t.Item1 == cmd)
.Select(t => t.Item2)
.FirstAsync();
sAction.OnNext(cmd);
return await result;
}