SimpleInjectorContainerAdapter, registering Sagas - rebus

Does the SimpleInjectorContainerAdapter support Sagas that are registered through the SimpleInjector container, using the following code I always get the exception;
type RebusPlaypen.MyMessageA, RebusPlaypen could not be dispatched to any handl
ers
The following code demonstrates the issue. Could this be that I am implementing the Saga registration incorrectly, or does the SimpleInjectorContainerAdapter not support this type of registration ?
using Rebus.Bus;
using Rebus.Config;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.Routing.TypeBased;
using Rebus.Sagas;
using Rebus.SimpleInjector;
using Rebus.Transport.InMem;
using SimpleInjector;
using System;
using System.Threading.Tasks;
// Rebus.3.1.2
// Rebus.SimpleInjector.3.0.0
namespace RebusPlaypen
{
public interface IMyDependency
{
void DoSomethingGood();
}
public class MyDependency : IMyDependency
{
public void DoSomethingGood()
{
Console.WriteLine("I've done something");
}
}
public class MyMessageA
{
public Guid CollationId { get; set; }
public string FaveIceCreamFlavour { get; set; }
}
public class MyMessageB
{
public Guid CollationId { get; set; }
public string FaveBand{ get; set; }
}
public class MyMessageSagaData : ISagaData
{
public Guid Id {get;set;}
public int Revision {get;set;}
public Guid CollationId {get;set;}
public bool HasFaveBand { get; set; }
}
public interface IMyMessageSaga : IAmInitiatedBy<MyMessageA>,
IHandleMessages<MyMessageB>
{
}
public class MyMessageSaga: Saga<MyMessageSagaData>,
IMyMessageSaga
{
readonly IMyDependency _myDependency;
readonly IBus _bus;
public MyMessageSaga(IMyDependency myDependency,
IBus bus)
{
_myDependency = myDependency;
_bus = bus;
}
protected override void CorrelateMessages(ICorrelationConfig<MyMessageSagaData> config)
{
config.Correlate<MyMessageA>(s => s.CollationId, d => d.CollationId);
config.Correlate<MyMessageB>(s => s.CollationId, d => d.CollationId);
}
public async Task Handle(MyMessageA message)
{
Console.WriteLine("Handled MyMessageA");
_myDependency.DoSomethingGood();
await _bus.Send(new MyMessageB { CollationId = message.CollationId, FaveBand = "Depeche Mode" });
await PossiblyPerformCompleteAction();
}
public async Task Handle(MyMessageB message)
{
Console.WriteLine("Handled MyMessageB");
_myDependency.DoSomethingGood();
Data.HasFaveBand = true;
await PossiblyPerformCompleteAction();
}
async Task PossiblyPerformCompleteAction()
{
if (Data.HasFaveBand)
{
MarkAsComplete();
}
}
}
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
container.Register<MyMessageSaga>(Lifestyle.Transient);
container.Register<IMyMessageSaga>(() => container.GetInstance<MyMessageSaga>(), Lifestyle.Transient);
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}
}

For completeness, the following changes allowed the code in the original question to work correctly with the Simple Injector container;
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
// The missing registration
container.RegisterCollection(typeof(IHandleMessages<>), new [] {Assembly.GetExecutingAssembly()});**
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}

No matter which IoC container you use, you must ensure that your handlers are resolved by the IHandleMessages<TMessage> implementations they provide.
If you try and
container.GetAllInstances<IHandleMessages<MyMessageA>>();
or
container.GetAllInstances<IHandleMessages<MyMessageB>>();
you will see that no handlers are returned. That's why Rebus cannot find any handlers to dispatch your messages to :)

Related

DbContext in Service triggered by Hangfire

I have a .NET 6 Razor Pages app that triggers background tasks and then informs the user of their status via SignalR.
I'm trying to use Database1 context in the PerformBackgroundJob method, but it's disposed. What technique should I use to inject Database1 context in PerformBackgroundJob, or how else can I get this to work?
namespace Toolkat.Pages
{
public class ProcessModel : PageModel
{
private readonly Database1Context _context;
private readonly ToolkatContext _tkcontext;
private IConfiguration configuration;
private readonly IQueue _queue;
private readonly IHubContext<JobHub> _hubContext;
static ServerConnection conn;
static Server server;
static Job job;
public ProcessModel(
Database1Context context,
ToolkatContext tkcontext,
IConfiguration _configuration,
IQueue queue,
IHubContext<JobHub> hubContext)
{
_context = context;
_tkcontext = tkcontext;
configuration = _configuration;
_queue = queue;
_hubContext = hubContext;
}
public IList<CustomFileImport> CustomFileImport { get; set; } = default!;
[BindProperty]
public CustomFileImport CustomFileImportNumberTwo { get; set; } = default!;
public async Task OnGetAsync()
{
if (_context.CustomFileImports != null)
{
CustomFileImport = await _context.CustomFileImports
.Include(c => c.FileImportType)
.Include(c => c.FileImportStatus)
.Where(i => i.FileImportStatusId.Equals(1))
.ToListAsync();
}
}
public async Task<IActionResult> OnPostAsync(int[] fileImportId)
{
//Generate GUID
Guid jobId = Guid.NewGuid();
//Update FileImportItems with GUID
foreach (var id in fileImportId)
{
if (/*id == null ||*/ _context.CustomFileImports == null)
{
return NotFound();
}
var customfileimport = await _context.CustomFileImports.FirstOrDefaultAsync(m => m.FileImportId == id);
if (customfileimport == null)
{
return NotFound();
}
customfileimport.ProcessId = jobId;
await _context.SaveChangesAsync();
}
_queue.QueueAsyncTask(() => PerformBackgroundJob(jobId));
return RedirectToPage("./Result", new { jobId });
}
private async Task PerformBackgroundJob(Guid jobId /*CancellationToken cancellationToken*/)
{
await _hubContext.Clients.Group(jobId.ToString()).SendAsync("progress", "PerformBackgroundJob Started");
/*
var customFileImports = await _context.CustomFileImports
.Include(c => c.FileImportType)
.Where(i => i.ProcessId.Equals(jobId))
.ToListAsync();
*/
Debug.WriteLine("ProviderName:" + _context.Database.ProviderName);
/*
foreach (var f in customFileImports)
{
await _hubContext.Clients.Group(jobId.ToString()).SendAsync("progress", WebUtility.HtmlEncode(f.FileName));
}
*/
}
}
}
I had to combine lessons from lots of articles to figure this out. Hangfire has a nice way of approaching this.
Replace
_queue.QueueAsyncTask(() => PerformBackgroundJob(jobId));
With
BackgroundJob.Enqueue<ProcessFilesService>(x => x.DoWork());
Passing dependencies
and create this class
public class ProcessFilesService
{
IServiceProvider _serviceProvider;
public ProcessFilesService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoWork()
{
using var scope = _serviceProvider.CreateScope();
var ctx = scope.ServiceProvider.GetRequiredService<MyDatabaseContext>();
using var hubScope = _serviceProvider.CreateScope();
var _hubContext = hubScope.ServiceProvider.GetRequiredService<JobHub>();
Debug.WriteLine(ctx.Database.ProviderName);
}
}
Hmm...I didn't need to register it as a service in program.cs and it appears to still be working. Will have to learn more about that.

unit text mock dbContext

I try to do a unitary test on a repository that returns a Ienumerable. But I have the next mistake:
System.AggregateException : One or more errors occurred. (The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.)
---- System.InvalidOperationException : The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
This my unit test :
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IDbAsyncEnumerable<User>>();
((IDbAsyncEnumerable<User>)mockSet).GetAsyncEnumerator()
.Returns(new TestDbAsyncEnumerator<User>(GetUserList().AsQueryable().GetEnumerator()));
((IQueryable<User>)mockSet).Provider.Returns(new TestDbAsyncQueryProvider<User>(GetUserList().AsQueryable().Provider));
((IQueryable<User>)mockSet).Expression.Returns(GetUserList().AsQueryable().Expression);
((IQueryable<User>)mockSet).ElementType.Returns(GetUserList().AsQueryable().ElementType);
((IQueryable<User>)mockSet).GetEnumerator().Returns(GetUserList().AsQueryable().GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
CancellationToken cancellationToken = new CancellationToken();
UserRepository userRepository = new UserRepository(mockContext);
var users = userRepository.GetListAsync(cancellationToken).Result;
//Assert
Assert.NotNull(users);
My repo I want to test :
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto
{
Id = u.Id,
FistName = u.FistName ,
LastName = u.LastName
}).ToListAsync(cancellationToken);
}
What is the problem ?
As covered in the OP comments the doco you're referring to is for EF, not EFCore. You need to implement a different set of interfaces.
The usual advice is to avoid mocking the DbContext however in this case you probably need to as the async operations aren't supported by the in-memory provider. I'm not sure if SQLite supports them. EntityFrameworkCore.Testing should handle this case (disclaimer, I am the author), but you'd need to use your context implementation rather than an interface.
The most common way to get this working is to create implementations of the async interfaces, in the same manner as the EF doco but for EFCore. You'll find most EFCore mocking libraries will do it this way:
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
This isn't a complete implementation, just what's needed to solve the OP case. The important bit is this line:
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
This is what is going to allow the projection to work with the async operation.
Working example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using AutoFixture;
using KellermanSoftware.CompareNetObjects;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;
using NSubstitute;
using NUnit.Framework;
namespace Question62783423
{
public class Tests
{
[Test]
public void Test1()
{
var fixture = new Fixture();
var users = new TestAsyncEnumerable<User>(fixture.CreateMany<User>());
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IAsyncEnumerable<User>>();
((IAsyncEnumerable<User>) mockSet).GetAsyncEnumerator().Returns(users.GetAsyncEnumerator());
((IQueryable<User>) mockSet).Provider.Returns(users.Provider);
((IQueryable<User>) mockSet).Expression.Returns(users.Expression);
((IQueryable<User>) mockSet).ElementType.Returns(users.ElementType);
((IQueryable<User>) mockSet).GetEnumerator().Returns(((IQueryable<User>) users).GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
var cancellationToken = new CancellationToken();
var userRepository = new UserRepository(mockContext);
var result1 = userRepository.GetListAsync(cancellationToken).Result;
var result2 = userRepository.GetListAsync(cancellationToken).Result;
var comparer = new CompareLogic();
comparer.Config.IgnoreCollectionOrder = true;
comparer.Config.IgnoreObjectTypes = true;
var comparisonResult1 = comparer.Compare(users, result1);
var comparisonResult2 = comparer.Compare(users, result2);
Assert.That(comparisonResult1.Differences.Any(), Is.False);
Assert.That(comparisonResult2.Differences.Any(), Is.False);
}
}
}
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
public class User
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IMyContext
{
DbSet<User> Users { get; set; }
}
public class UserRepository
{
private readonly IMyContext _myContext;
public UserRepository(IMyContext myContext)
{
_myContext = myContext;
}
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto { Id = u.Id, FirstName = u.FirstName, LastName = u.LastName }).ToListAsync(cancellationToken);
}
}

Automapper - Mapper already initialized error

I am using AutoMapper 6.2.0 in my ASP.NET MVC 5 application.
When I call my view through controller it shows all things right. But, when I refresh that view, Visual Studio shows an error:
System.InvalidOperationException: 'Mapper already initialized. You must call Initialize once per application domain/process.'
I am using AutoMapper only in one controller. Not made any configuration in any place yet nor used AutoMapper in any other service or controller.
My controller:
public class StudentsController : Controller
{
private DataContext db = new DataContext();
// GET: Students
public ActionResult Index([Form] QueryOptions queryOptions)
{
var students = db.Students.Include(s => s.Father);
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
});
return View(new ResulList<StudentViewModel> {
QueryOptions = queryOptions,
Model = AutoMapper.Mapper.Map<List<Student>,List<StudentViewModel>>(students.ToList())
});
}
// Other Methods are deleted for ease...
Error within controller:
My Model class:
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual Father Father { get; set; }
public virtual Sarparast Sarparast { get; set; }
public virtual Zamin Zamin { get; set; }
public virtual ICollection<MulaqatiMehram> MulaqatiMehram { get; set; }
public virtual ICollection<Result> Results { get; set; }
}
My ViewModel Class:
public class StudentViewModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual FatherViewModel Father { get; set; }
public virtual SarparastViewModel Sarparast { get; set; }
public virtual ZaminViewModel Zamin { get; set; }
}
If you want/need to stick with the static implementation in a unit testing scenario, note that you can call AutoMapper.Mapper.Reset() before calling initialize. Do note that this should not be used in production code as noted in the documentation.
Source: AutoMapper documentation.
When you refresh the view you are creating a new instance of the StudentsController -- and therefore reinitializing your Mapper -- resulting in the error message "Mapper already initialized".
From the Getting Started Guide
Where do I configure AutoMapper?
If you're using the static Mapper method, configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.asax file for ASP.NET applications.
One way to set this up is to place all of your mapping configurations into a static method.
App_Start/AutoMapperConfig.cs:
public class AutoMapperConfig
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
...
});
}
}
Then call this method in the Global.asax.cs
protected void Application_Start()
{
App_Start.AutoMapperConfig.Initialize();
}
Now you can (re)use it in your controller actions.
public class StudentsController : Controller
{
public ActionResult Index(int id)
{
var query = db.Students.Where(...);
var students = AutoMapper.Mapper.Map<List<StudentViewModel>>(query.ToList());
return View(students);
}
}
I've used this method before and it worked till version 6.1.1
Mapper.Initialize(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())
);
Since version 6.2, this doesn't work any more. To correctly use Automapper create a new Mapper and us this one like this:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())).CreateMapper();
var model = mapper.Map<ContactModel>(this);
In case you really need to "re-initialize" AutoMapper you should switch to the instance based API to avoid System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.
For example, when you are creating the TestServer for xUnit tests you can just set ServiceCollectionExtensions.UseStaticRegistration inside fixure class constructor to false to make the trick:
public TestServerFixture()
{
ServiceCollectionExtensions.UseStaticRegistration = false; // <-- HERE
var hostBuilder = new WebHostBuilder()
.UseEnvironment("Testing")
.UseStartup<Startup>();
Server = new TestServer(hostBuilder);
Client = Server.CreateClient();
}
For Unit Testing, you can add Mapper.Reset() to your unit test class
[TearDown]
public void TearDown()
{
Mapper.Reset();
}
You can use automapper as Static API and Instance API ,
Mapper already initialized is common issue in Static API , you can use mapper.Reset()
where you initialized mapper but this this not an answer at all.
Just try with instance API
var students = db.Students.Include(s => s.Father);
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Student, StudentViewModel>();
});
IMapper iMapper = config.CreateMapper();
return iMapper.Map<List<Student>, List<StudentViewModel>>(students);
Automapper 8.0.0 version
AutoMapper.Mapper.Reset();
Mapper.Initialize(
cfg => {
cfg.CreateMap<sourceModel,targetModel>();
}
);
You can simply use Mapper.Reset().
Example:
public static TDestination MapToObject<TSource, TDestination>(TSource Obj)
{
Mapper.Initialize(cfg => cfg.CreateMap<TSource, TDestination>());
TDestination tDestination = Mapper.Map<TDestination>(Obj);
Mapper.Reset();
return tDestination;
}
If you are using MsTest you can use the AssemblyInitialize attribute so that mapping gets configured only once for that assembly (here test assembly). This is generally added into to the base class of controller unit tests.
[TestClass]
public class BaseUnitTest
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.EmailAddress));
});
}
}
I hope this answer helps
If you are using Mapper in UnitTest and your tests more then one, You may use Mapper.Reset()
`
//Your mapping.
public static void Initialize()
{
Mapper.Reset();
Mapper.Initialize(cfg =>
{
cfg.CreateMap<***>
}
//Your test classes.
[TestInitialize()]
public void Initialize()
{
AutoMapping.Initialize();
}`
private static bool _mapperIsInitialized = false;
public InventoryController()
{
if (!_mapperIsInitialized)
{
_mapperIsInitialized = true;
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Inventory, Inventory>()
.ForMember(x => x.Orders, opt => opt.Ignore());
}
);
}
}

Unit tests fails after upgrading to .net core 2

Can someone maybe explain to me what this means and why am i getting it.
System.InvalidOperationException : When called from 'VisitLambda',
rewriting a node of type 'System.Linq.Expressions.ParameterExpression'
must return a non-null value of the same type. Alternatively, override
'VisitLambda' and change it to not visit children of this type.
I am getting it from my unit tests I am running the latest .net core 2 with EF core. all my tests were fine till i upgraded then i started getting the error.
The funny thing is, is that when i run the project the line were it fails in the the tests is ok.
This is my Test
[Fact]
public async Task GetUserProfileAsync_Where_Employee_Exist_Test()
{
// Given
var user = TestPrincipal.CreatePrincipalForEmployeeUser();
using (var factory = new TestContextFactory())
using (var context = factory.CreateInMemoryDatabase<ApplicationContext>())
{
this.SetDependencies(context);
var data = EmployeeValueHelper.GetEmployeeValues();
context.AddRange(data);
context.SaveChanges();
var sut = new ProfileService(new DbContextRepository<Data.Models.Employees.Employee>(context), this.userService, this.moqEmploymentStatusService.Object);
// When
// -> this method goes to a service and calls the below FindByIdAsync
var actual = await sut.GetProfileForUserAsync(user);
// Then
Assert.Equal(10, actual.EmployeeId);
}
}
public async Task<Employee> FindByIdAsync(long id)
{
var profile = await this.repository.Set
.Include(_ => _.Address) --> IT FAILS ON THIS LINE, IF I REMOVE THE INCLUDE THEN IT WORKS
.Include(_ => _.EmployeeImage)
.SingleOrDefaultAsync(_ => _.EmployeeId == id);
if (profile == null)
{
return null;
}
return profile;
}
UPDATE
Service Layer
public class ProfileService : GenericService<Employee>, IProfileService
{
private readonly DbContextRepository<Employee> repository;
private readonly IUserService userService;
public ProfileService(DbContextRepository<Employee> repository, IUserService userService)
: base(repository)
{
this.repository = repository;
this.userService = userService;
}
public Task<Employee> GetProfileForUserAsync(ClaimsPrincipal user)
{
var id = this.userService.GetEmployeeId(user);
return id.HasValue ? this.FindByIdAsync(id.Value) : null;
}
public async Task<Employee> FindByIdAsync(long id)
{
var profile = await this.repository.Set
.Include(_ => _.Address)
.Include(_ => _.EmployeeImage)
.SingleOrDefaultAsync(_ => _.EmployeeId == id);
if (profile == null)
{
return null;
}
return profile;
}
}
Employee Model
public class Employee : IValidatableObject
{
[Key]
[Column("pkEmpID")]
public long EmployeeId { get; set; }
[Column("fkCompanyID")]
public long CompanyId { get; set; }
public virtual Company Company { get; set; }
[Display(Name = "lblEmpNumber")]
public string EmpNumber { get; set; }
public virtual IList<Address> Address { get; set; } = new List<Address>();
// WITH SOME EXTRA STUFF NOT NEEDED FOR THIS
}
Repository
public class DbContextRepository<TEntity> : IGenericRepository<TEntity>, IDisposable
where TEntity : class
{
public DbContextRepository(ApplicationContext context)
{
this.Context = context;
this.Set = context.Set<TEntity>();
this.SetWithNoTracking = this.Set.AsNoTracking();
}
public ApplicationContext Context { get; }
public DbSet<TEntity> Set { get; }
public IQueryable<TEntity> SetWithNoTracking { get; }
// WITH SOME EXTRA STUFF NOT NEEDED FOR THIS
}
Hope this will shed more light

Moq callback with invoking parameter

I have these three lines of c# code using Moq, how can I write a single line?
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Success)).Callback(() => statuses.Add(JobStatus.Success));
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Failed)).Callback(() => statuses.Add(JobStatus.Failed));
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Running)).Callback(() => statuses.Add(JobStatus.Running));
Thanks for the help.
There is a piece of code you are asking for
JobQueueRepository
.Setup(it => it.UpdateJobQueueStatus(DefaultJobId, It.IsAny<JobStatus>()))
.Callback<int, JobStatus>((id, status) => statuses.Add(status));
And a test that tests how it works
[TestClass]
public class TestClass
{
[TestMethod]
public void TestMethod()
{
var statuses = new List<JobStatus>();
var JobQueueRepository = new Mock<IJobQueueRepository>();
int DefaultJobId = 100500;
JobQueueRepository
.Setup(it => it.UpdateJobQueueStatus(DefaultJobId, It.IsAny<JobStatus>()))
.Callback<int, JobStatus>((id, status) => statuses.Add(status));
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Failed);
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Running);
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Success);
statuses.Should().HaveCount(3);
statuses.Should().Contain(JobStatus.Failed);
statuses.Should().Contain(JobStatus.Running);
statuses.Should().Contain(JobStatus.Success);
}
public enum JobStatus
{
Success,
Failed,
Running
}
public interface IJobQueueRepository
{
void UpdateJobQueueStatus(int id, JobStatus status);
}
}
You can easily create an extension method to do that as below.
public class Class1
{
[Test]
public void CallBackDemo() {
var statuses = new List<JobStatus>();
var jobQueueRepositoryStub = new Mock<IJobQueueRepository>();
const int defaultJobId = 100500;
jobQueueRepositoryStub.Setup(r => r.UpdateJobQueueStatus(defaultJobId, JobStatus.Success))
.Callback( new Action[]
{
() => statuses.Add(JobStatus.Success),
() => statuses.Add(JobStatus.Failed),
() => statuses.Add(JobStatus.Running)
});
var sut = new Sut(jobQueueRepositoryStub.Object);
sut.Do(defaultJobId);
Assert.True(statuses.Count == 3);
Assert.True(statuses.Any(x => x == JobStatus.Failed));
Assert.True(statuses.Any(x => x == JobStatus.Running));
Assert.True(statuses.Any(x => x == JobStatus.Success));
}
Callback extension method:
public static class Ext
{
public static void Callback<TRepo>(this ISetup<TRepo> repo, IEnumerable<Action> actions ) where TRepo : class {
foreach (var action in actions) {
action();
}
}
}
Sut (System Under Test) and other classes:
public enum JobStatus { Success, Failed, Running }
public interface IJobQueueRepository {
void UpdateJobQueueStatus(int id, JobStatus status);
}
public class Sut {
private readonly IJobQueueRepository _repository;
public Sut(IJobQueueRepository repository) {
_repository = repository;
}
public void Do(int jobId) {
_repository.UpdateJobQueueStatus(jobId, JobStatus.Success);
_repository.UpdateJobQueueStatus(jobId, JobStatus.Failed);
_repository.UpdateJobQueueStatus(jobId, JobStatus.Running);
}
}

Resources