Every time I create a new object of type in Akka.NET. The entire Journal of that type gets loaded in the constructor
My test:
[Test, Category("Integration")]
public async Task Should_Persist_Actor()
{
var model = Mocks.Fake.Contact();
await Actors.ContactActor.Ask(new CreateContactCommand(model, "unit test", DateTime.Now));
var context = new MyTestContext("xxx");
using (context)
{
var found = context.Set<Contact>().FirstOrDefault(x => x.Id == model.Id);
Assert.IsNotNull(found);
}
}
This is the constructor that keeps getting hit, once for every entry in the journal
public sealed class CreateContactCommand : AuditCommandBase<Contact, CreateContactEvent>, ICommand<Contact, CreateContactEvent>
{
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
public CreateContactCommand(Contact obj, string auditUser, DateTime auditTime) : base(obj, auditUser, auditTime)
{
// This gets hit for everything in the journal db
_logger.Debug("Create Contact Command Ctor");
}
}
If i truncate the journal my test passes right away. If there is data in the journal, it hits the type's constructor for each item...
That is working as intended. When you bring an akka actor online, it will restore it's state by replaying the event journal.
Related
I am creating an integration test to check that the data is working based on this very good tutorial.
The tutorial loads sample data in the OnModelCreating. But I was unsure if doing that will repeatedly load data to the DB when running the program.
However although I can get the index page to load, it has the page content, such as the table structure for the data it doesn't have the data from the database.
Using Swagger I copied a sample of data as JSON, saved it to a file, capitalized the first letter of the key to make it the same as the properties (after not doing do was fruitless as well), and tried to add it to the context.
internal static class AddTestData
{
//import json array and add to context
public static void AddMovieData(ApplicationDbContext context)
{
var jsonString = File.ReadAllText("testMoviedata.json");
var list = JsonSerializer.Deserialize<List<Movie>>(jsonString);
{
foreach (var item in list)
{
context.Movie.Add(item);
}
context.SaveChanges();
}
}
}
and tried to add it to the dbcontext in this process in the WebApplicationFactory Class from HERE
public class TestingWebAppFactory<TEntryPoint> : WebApplicationFactory<Program> where TEntryPoint : Program
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
......... stuff deleted for brevity...
using (var appContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
try
{
appContext.Database.EnsureCreated();
// Seed the database with test data.
AddTestData.AddMovieData(appContext);
}
catch (Exception ex)
{
//Log errors or do anything you think it's needed
throw;
}
}
... still nothin. Page loads, no data loads.
Also why can't I get breakpoints to work in the Integration project?
What am I doing wrong?
Solved!!!
The code was OK,but the data wasn't being deserialised.
I had to move it to the main project and test it there.
The solution is
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var list = JsonSerializer.Deserialize<Movie[]>(jsonString, options);
I want to switch my code to an async implementation. When I want to do this then I notice that my related data gets not set automatically after I retrieve them like it used to do it.
This is the initial function that gets called from an API controller. I used the AddDbContext function to add the dbcontext class via dependency injection into my controller:
public async Task<Application> GetApplicationById(AntragDBNoInheritanceContext dbContext, int id)
{
List<Application> ApplicationList = await dbContext.Applications.FromSqlRaw("Exec dbo.GetApplication {0}", id).ToListAsync();
Application Application = ApplicationList.First();
if(Application != null)
{
await CategoryFunctions.GetCategoryByApplicationID(Application.Id);
}
}
The GetCategoryByApplicationId function loads the related category of an application which is a many to one relation between Category and Application:
public async Task<Category> GetCategoryByApplicationID(int applicationID)
{
var optionsBuilder = new DbContextOptionsBuilder<AntragDBNoInheritanceContext>();
optionsBuilder.UseSqlServer(ApplicationDBConnection.APPLICATION_CONNECTION);
using (var dbContext = new AntragDBNoInheritanceContext(optionsBuilder.Options))
{
List<Category> category = await dbContext.Categories.FromSqlRaw("Exec GetApplicationCategory {0}", applicationID).ToListAsync();
if (category.Any())
{
return category.First();
}
}
return null;
}
When I want to retrieve an application then the field Category is not set. When I did not use async/await it would set the category automatically for me. Of course I could just return the Category Object from the GetCategoryByApplicationId and then say:
Application.Category = RetrievedFromDbCategory;
But this seems a bit unmaintainable compared to the previous behaviour. Why does this happen now and can I do something about it? Otherwise I don't see much benefits on using async/await .
I am trying to unit test Microsoft.Azure.ServiceBus(3.3.0) topic and subscription functionality. But I am not interested in testing Microsoft.Azure.ServiceBus classes, but more how mock Send a message to the topic and check if that message exists on that specific topic with a subscription.
At the moment I have a super simple Publisher class with one method SendAsync. As you can see here:
// Pseudo code, not full implementation!
public class Publisher : IPublisher
{
private readonly ManagementClient _managementClient;
private readonly TopicClientFactory _topicClientFactory;
public Publisher(ManagementClient managementClient, TopicClientFactory topicClientFactory)
{
_managementClient = managementClient;
_topicClientFactory = topicClientFactory;
}
public async Task SendAsync(myModel message)
{
ITopicClient topicClient = _topicClientFactory.Create("MyTopic");
// encode message using message
Message message = new Message(encodedMessage);
await topicClient.SendAsync(message); // trying to mock & test this!
await topicClient.CloseAsync();
}
}
Factory has only one method. When creating a new TopicClient using factory I am also returning the ITopicClient interface. Not sure if that helps.
// Pseudo code, not full implementation!
public class TopicClientFactory
{
public ITopicClient Create(string topicPath)
{
return new TopicClient("MyConnectionString", topicPath);
}
}
Unit test:
[Fact]
public async Task Name()
{
var managementClientMock = new Mock<ManagementClient>("MyConnectionString");
var topicClientFactoryMock = new Mock<TopicClientFactory>("MyConnectionString");
// mock topic client's send method!
var topicClientMock = new Mock<ITopicClient>();
topicClientMock.Setup(x =>
x.SendAsync(It.IsAny<Message>())).Returns(Task.CompletedTask); // .Verifiable();
// pass mocked topicClient to mocked factory
topicClientFactoryMock.Setup(tc => tc.Create("topicPath")).Returns(topicClientMock.Object);
var publisher = new Publisher(managementClientMock.Object, topicClientFactoryMock.Object);
await publisher.SendAsync(command);
// how to test if message has been sent?
}
In a ASP NET Controller i have a service that returns a list of items.This service serves from the RAM the list to requesters.
The list can also be altered by a special group of users , so everytime it is altered i write the changes to disk and update my RAM from disk. (Reading my own writes this way)
From a JS client when i alter this list , the changes are written correctly on the disk , but when i forward a second request to get my list , i am served a stale list.I need to hit F5 for the client to get the right data.
I do not understand how does the RAM cache lags behind.
You can see in my service below that i have guarded the altering method with a lock.I have also tried without it to no avail.
Service
public class FileService : IADReadWrite {
private const int SIZE = 5;
private const string COMPUTER_FILE = #"computers.txt";
private List<Computer> computers = new List<Computer>();
private readonly object #filelock = new object();
private readonly Computer[] DEFAULT_COMPUTERS_LIST = new Computer[] {
new Computer(id:"W-CZC81371RS",Username:"A"),
new Computer(id:"W-CZC81371RQ",Username:"B"),
};
async Task<Computers> GetComputersAsymc() {
if (this.computers.Count == 0) {
var query = await Fetch();
this.computers = query.ToList();
}
var result = new Computers(this.computers);
return result;
}
public async Task<bool> AddComputerAsync(Computer computer) {
lock (filelock) {
if (this.computers.Any(x => x == computer)) {
return false;
}
this.computers.Add(computer);
File.WriteAllText(COMPUTER_FILE, JsonConvert.SerializeObject(this.computers, Formatting.Indented));
this.computers = JsonConvert.DeserializeObject<List<Computer>>(File.ReadAllText(COMPUTER_FILE));
}
return true;
}
---------------------Helpers --------------------------
private async Task<IEnumerable<Computer>> Fetch() {
if (!File.Exists(COMPUTER_FILE)) {
WriteComputersToDisk();
}
using (FileStream stream = new FileStream(COMPUTER_FILE, FileMode.Open, FileAccess.Read)) {
var raw = await File.ReadAllTextAsync(COMPUTER_FILE);
var comp = JsonConvert.DeserializeObject<List<Computer>>(raw);
return comp;
}
}
private void WriteComputersToDisk() {
var comps = DEFAULT_COMPUTERS_LIST;
var data = JsonConvert.SerializeObject(comps, Formatting.Indented);
File.WriteAllText(COMPUTER_FILE, data);
}
}
Controller
public class MyController:Controller
{
MyController(IADReadWrite service)
{
this.service=service;
}
IADReadWrite service;
[HttpGet]
public async Task<List<Computer>> GetAll()
{
return await service.GetComputersAsync();
}
[HttpPost]
public async Task AddComputer(Computer computer)
{
await service.AddComputerAsync(computer);
}
}
Scenario
Initial list : [0,1]
Client hits controller calling `AddComputer` {2}
I check the file , list is now: [0,1,2]
Client hits controller calling `GetComputers` -> it returns [0,1]
I hit F5 on the browser -> GetComputers gets hit again -> it returns [0,1,2]
P.S
I have not posted the Computer class since it does not matter in this scenario ( It implements IEquateable in case you are wondering if it is failing when i use the == operator.
The last 2 methods deal with the initialization of the Disk file.
I have written a code for index creation but when i run the application and try to call it then give a error of 'There is no index named'. My code is as follow :
I have created a class for index creation like :
public class TicketsByPaymentTotal : AbstractIndexCreationTask<Tickets,TicketTotal>
{
public TicketsByPaymentTotal()
{
Map = docs => from doc in docs
select new
{
TicketId = doc.TicketData.ID,
TicketTotalVal = doc.TicketData.PaymentTotal,
TotalVal = doc.TicketData.Total
};
Reduce = results => from result in results
group result by result.TicketId
into g
select new
{
TicketId = g.Key,
TicketTotalVal = g.Sum(x => x.TicketTotalVal),
TotalVal = g.Sum(x => x.TotalVal)
};
}
}
and i am creating index in global.asax file like :
public class MvcApplication : System.Web.HttpApplication
{
public IDocumentSession DocSession;
//DocumentStore store = new DocumentStore{ConnectionStringName="RavenDB2",DefaultDatabase="Dinerware"};
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
DataDocumentStore.Initialize();
DocSession = DataDocumentStore.Instance.OpenSession("Dinerware");
RegisterRoutes(RouteTable.Routes);
//store.Initialize();
HandlerConfig.RegisterHandlers(GlobalConfiguration.Configuration.MessageHandlers);
IndexCreation.CreateIndexes(typeof(TicketsByPaymentTotal).Assembly,DocSession.Advanced.DocumentStore);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller="Home",action="Index",id=""});
}
}
How to solve this issue.
Thanks.
Regards,
sandy
You are creating the index in the system database, even though you are using a named instance called "Dinnerware".
If you want to use a named database, pass it in the DefaultDatabase parameter when you create the document store instance.
Don't pass the name when you are opening the session. That should be reserved for when you have multi-database needs.
You should just pass your document store instance into the CreateIndexes method directly, rather than pulling it out of session.
Don't open a session and assign it to a property. Sessions are meant to be short lived, and must be disposed. Only the document store should be long lived on a single instance. Usually, sessions are created in a using statement. In a web app, a new session should be created for each and every web request.