I'm using Microsoft.EntityFrameworkCore.InMemory/3.1.16.
my tests pass when I run them individually but when I run all tests some of them fail.
Sdk :.net core 3.1
here is my optionbuilder configuration:
public static DbContextOptionsBuilder<T> GetDbContextOptionsBuilder<T>( string? connectionString =null , Guid? databaseId = null)
where T : DbContext
{
var optionBuilder = new DbContextOptionsBuilder<T>();
if (databaseId is null)
databaseId = Guid.NewGuid();
optionBuilder.UseInMemoryDatabase( $"dbApi-{databaseId:N}",
x => {
x.UseHierarchyId();
} )
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
return optionBuilder;
}
public static DbContextOptionsBuilder<T> GetInDbContextOptionsBuilder<T>( IConfiguration configuration )
where T : DbContext
{
configuration ??= ConfigHelper.GetConfig();
return GetDbContextOptionsBuilder<T>( configuration.GetConnectionString( "DefaultConnection" ) );
}
and how my unit tests call dbContext like this:
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options);
in tests I seed some data like this:
db.SeedTemplate(); //test 1
db.SeedCategory(); //test 2
when I run the tests it throws following error:
The instance of entity type 'entity' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Edit 1:
I add two sample tests:
//This test will pass
[Fact]
public async Task Can_Add_Template_With_Attribute()
{
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options);
//priceAttribute and displayAttribute removed for brevity
template.LinkAttributes( new List<Attribute>{ priceAttribute,displayAttribute} );
await db.Templates.AddAsync( template);
await db.SaveChangesAsync();
var actualTemplate = await db.Templates.FirstAsync();
actualTemplate.ShouldNotBeNull();
actualTemplate.Attributes.First().ShouldEqual( priceAttribute ););
}
//This test will throw error
[Fact]
public async Task Can_Delete_Template()
{
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options, MediatorHelper.Initialize());
//priceAttribute and displayAttribute removed for brevity
template.LinkAttributes( new List<Attribute>{ priceAttribute,displayAttribute} );
await db.Templates.AddAsync( template);
await db.SaveChangesAsync();
template.Delete();
var command = new DeleteTemplateCommand( template.Id );
var handler = new DeleteTemplateCommand.DeleteTemplateCommandHandler( db );
var result=await handler.Handle( command,CancellationToken.None );
result.Should().BeSuccess();
var actualTemplate = await db.Templates.FirstAsync();
actualTemplate.IsDeleted.ShouldBeTrue();
}
Related
I have one AsyncPeriodicBackgroundWorkerBase base class(DataValidateWorker) which runs 1 minute interval.
I need to send the data I get from the DB to a third party web service and update the results in the db. A Web service response arrives in about 30-40 seconds. For this reason, I need to send Web service queries simultaneously, not sequentially.
For this reason, I wrote code in accordance with parallel programming as seen below. I cannot pull the database connection for the Task I wrote. DB connection closed, I got many errors like Executing.
How can I create the db connection for my Task?
Would it be better to write this job in an external application (exe or service) instead of ABP?
public class DataValidateWorker : AsyncPeriodicBackgroundWorkerBase
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IDataFilter _dataFilter;
public DataValidateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory, IDataFilter dataFilter, IUnitOfWorkManager unitOfWorkManager) : base(timer, serviceScopeFactory)
{
_dataFilter = dataFilter;
_unitOfWorkManager = unitOfWorkManager;
Timer.Period = 60 * 1000; // 60 seconds
}
[UnitOfWork]
protected async override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
try
{
var notificationValidationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<NotificationValidation, int>>();
var notificationValidationItems = await notificationValidationRepository.GetQueryableAsync();
List<NotificationValidation> list = new List<NotificationValidation>();
using (var uow = _unitOfWorkManager.Begin())
{
using (_dataFilter.Disable<IMultiTenant>())
{
list = notificationValidationItems.Where(x => x.RecordDateTime <= DateTime.Now && x.ValidationResult == (int)ValidationResult.NotStarted).ToList();
}
}
NotificationValidationArgs jobArgs = new NotificationValidationArgs();
foreach (var item in list)
{
jobArgs.notificationValidationId = item.Id;
Task taskA = Task.Factory.StartNew(async (Object obj) =>
{
// doing some third party web service operations and db operations
}, jobArgs);
}
}
catch (Exception ex)
{
Logger.LogCritical(2001, ex, DateTime.Now.ToString() + " -> DataValidateWorker -> try 1 -> RDMS uow");
}
}
}
You don't await any of tasks, so lifetime of object ends while your task is still running.
Try to store all of the tasks in a collection and await them before method execution finishes.
Something like below:
public class DataValidateWorker : AsyncPeriodicBackgroundWorkerBase
{
public DataValidateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory)
{
}
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
var tasks = new List<Task>();
foreach (var item in list)
{
tasks.Add(YourLongJob(arg)); // don't await here. collect in a collection
}
await Task.WhenAll(tasks); // wait until all of them is completed.
}
private async Task YourLongJob(object arg)
{
await Task.Delay(30 * 1000); // a long job
}
}
So Initially I created a table named "TABLE" in the database. I wanted to add another table. So I added another query to create a table. However I get an error saying "TABLE1 does not exist" when I run the app.
I do feel like there is a flaw in my code. I think the _onCreate() method will only be called the first time I run the app. So any code I add on _onCreate() method afterwards will not run. Any help will be appreciated.
class DBHelper {
static Database _db;
static const String DB_NAME = 'employeeDB';
static const String ID = 'id';
static const String NAME = 'name';
static const String TABLE = 'Employee';
static const String ID1 = 'id1';
static const String NAME1 = 'name1';
static const String TABLE1 = 'Employee1';
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DB_NAME);
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int version) async {
await db.execute("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY,$NAME TEXT)");
await db.execute("CREATE TABLE $TABLE1 ($ID1 INTEGER PRIMARY KEY,$NAME1 TEXT)");
}
Future<Employee> save(Employee employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE, employee.toMap());
return employee;
}
Future<Employee1> saveEmp1(Employee1 employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE1, employee.toMap());
return employee;
}
Future<List<Employee>> getEmployees() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE, columns: [ID, NAME]);
List<Employee> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
Future<List<Employee1>> getEmployees1() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE1, columns: [ID1,
NAME1]);
List<Employee1> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
}
The first time run this app and initDb() in emulator, the db file employeeDB has created.
and it will not be created again
For only test app execution,
you can change
String DB_NAME = 'employeeDB'
to another name
String DB_NAME = 'employeeDB1'
or you can uninstall this app from Emulator first then run it again.
source code snippet of https://github.com/tekartik/sqflite/blob/master/sqflite/lib/sqlite_api.dart
/// Called when the database is created.
OnDatabaseCreateFn onCreate;
For schema migration, you can use OnUpgrade, detail reference https://efthymis.com/migrating-a-mobile-database-in-flutter-sqlite/
code snippet
await openDatabase(path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(initialSchema));
},
onUpgrade: (Database db, int oldVersion, int newVersion) async {
await db.execute(migrationScript));
});
You have to create migrations and run them against your existing database using the onUpgrade handler. Basically you need to check the existing database version and upgrade if the version is smaller than the migration number.
You can check out the detail steps/code tutorial here.
I needed to create more than just multiple tables; but multiple database. I have since developed a dart package; sqlite_at_runtime that stretches to as far as creating all these entities at run time.
Below is an example code that creates three tables with similar attributes.
await Sqlartime.tableCreate(['sample1','sample2','sample3'],['name TEXT','age INTEGER','temp REAL']);
My first foray into mocking in a WPF application. The code I'm testing is part of an MVVM ViewModel Method and looks like this:
try
{
var airframesForRegistration = this.UnitOfWork.Airframes.GetAirframesForRegistration(this.SearchRegistration);
this.currentAirframes = new ObservableCollection<Airframe>(airframesForRegistration);
}
catch (Exception ex)
{
this.logger.Error($"Could not access the database", ex);
throw;
}
I want to test that
the error is written to the logger service and
that the exception is thrown.
To do this I'm using XUnit and Moq thus:
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail()
{
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
DataException exception = new DataException();
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
Assert.Throws<DataException>(() => afrvm.GetAirframesForSearchRegistration());
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
The test fails thus:
Message: Moq.MockException :
Expected invocation on the mock exactly 1 times, but was 0 times: x => x.Error("Could not access the database'", System.Data.DataException: Data Exception.)
Configured setups:
ILog x => x.Error("Could not access the database", System.Data.DataException: Data Exception.)
Performed invocations:
ILog.Warn("No Operators found in the database")
ILog.Warn("No airframe statuses found in the database")
ILog.Error("Could not access the database", System.Data.DataException: Data Exception.
at Moq.MethodCall.Execute(Invocation invocation) in C:\projects\moq4\src\Moq\MethodCall.cs:line 120
(NB The extra ILog warnings occur elsewhere in the ViewModel and I was expecting those).
Question
This implies that the error logging was invoked and yet the test fails because it was invoked zero times! How can Moq and XUnit be set up to correctly test for this scenario?
The setup of the logger arguments is the problem.
Differing instances between what is thrown and what is expected mean they wont match when the mock is invoked.
The mock unit of work is throwing a new exception. Not the one you are expecting.
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
DataException exception = new DataException();
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(exception);
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
}
For a looser expectation you could have use It.IsAny<> argument matchers
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, It.IsAny<DataException>()));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, It.IsAny<DataException>()), Times.Exactly(1));
}
}
I am not good with Web API. Here is my problem. I send an Json serialized object from my Windows Form Application. The object is an Entity table. When I do a get response it returns a 500 server error. Basically I plan to have multiple post methods in one controller which I may not be doing right. So I need you guys to guide me on what I have been doing wrong.
Here is my Controller:
[ResponseType(typeof(HttpWebResponse)), HttpPost, ActionName("MerchandiseApi")]
public HttpResponseMessage PostMain(IList<IMF_Main> mainFromConsolidator)
{
if (!ModelState.IsValid)
return Request.CreateResponse(HttpStatusCode.BadRequest, 2);
using (var anthill = new AnthillConsolidatorEntities())
{
var main = new IMF_Main();
foreach (var item in mainFromConsolidator)
{
main.BrandID = item.BrandID;
main.ItemID = item.ItemID;
main.CategoryID = item.CategoryID;
main.SubCategoryID = item.SubCategoryID;
main.ClassID = item.ClassID;
main.GenderID = item.GenderID;
main.CoaID = item.CoaID;
main.SubCoaID = item.SubCoaID;
main.First_SRP = item.First_SRP;
main.Current_SRP = item.Current_SRP;
main.Previous_SRP = item.Previous_SRP;
main.isSenior = item.isSenior;
main.isActive = item.isActive;
main.DateCreated = item.DateCreated;
anthill.IMF_Main.Add(main);
anthill.SaveChanges();
}
}
return Request.CreateResponse(HttpStatusCode.OK, 1);
}
Here's my WebApiConfig:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "MerchandiseApi",
routeTemplate: "api/{controller}/{action}"
);
}
Here is where the Uri gets built: I have 2 more tables to send but I will start with this. This goes to my first Post method to the server
var jsonMain = JsonConvert.SerializeObject(consolidatorEntities.IMF_Main, Formatting.None);
HttpPost("http://localhost:50826/api/Merchandise/PostMain", jsonMain) == 1.ToString()
public string HttpPost(string uri, string json)
{
string content = "";
try
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.Accept = "application/json";
request.ContentType = "application/json";
byte[] bodyBytes = Encoding.UTF8.GetBytes(json);
request.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
request.GetRequestStream().Close();
var response = (HttpWebResponse)request.GetResponse();
var sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncod
ing("UTF-8"));
content = sr.ReadToEnd();
sr.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error sending data to Anthill \nException: " + ex, "Monytron - Consolidator", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return content;
}
Problem
The main problem is with your routing. Routes will check in order so when you post a request to http://localhost:50826/api/Merchandise/PostMain and you have these routes in order:
"api/{controller}/{id}"
"api/{controller}/{action}"
So the first route will match:
If your PostMain method is the only action with [HttpPost], then mainFromConsolidator will be null in your foreach loop you will receive a NullReferenceException that result in a 500 error.
If you have multiple method decorated with [HttpPost], then the call is ambiguous between those actions and you will receive an InvalidOperationExpception with "Multiple actions were found that match the request" message that result in a 500 error.
The other problem is you are using an ActionName("MerchandiseApi") but didn't post to that action.
Solution
You can use multiple solutions. As an option you can define only one route:
"api/{controller}/{action}/{id}"
This way you can create a controller that contains actions like these:
public class SomeController
{
// matches GET /api/some/action1
[HttpGet]
public HttpResponseMessage Action1()
// matches GET /api/some/action2/5
[HttpGet]
public HttpResponseMessage Action2(int id)
// matches POST /api/some/action3
[HttpPost]
public HttpResponseMessage Action3(SomeType someParameter)
// matches POST /api/some/action4
[HttpPost]
public HttpResponseMessage Action4(SomeType someParameter)
}
Anyway if you decide to define multiple routes, pay attention that routes will match in order and also if you used ActionName attribute, then use that name in url to call that action.
In a Silverlight 5 application in Dynamics CRM 2011 I access the Organization Service of the CRM to query for entity Metadata. I wrote a service that takes an entity name and returns a list of all its fields.
How can I test this service method automatically? The main problem is how to obtain a reference to the organization service from a Silverlight app that does not run in the context of the CRM.
My Service method looks like this:
public IOrganizationService OrganizationService
{
get
{
if (_organizationService == null)
_organizationService = SilverlightUtility.GetSoapService();
return _organizationService;
}
set { _organizationService = value; }
}
public async Task<List<string>> GetAttributeNamesOfEntity(string entityName)
{
// build request
OrganizationRequest request = new OrganizationRequest
{
RequestName = "RetrieveEntity",
Parameters = new ParameterCollection
{
new XrmSoap.KeyValuePair<string, object>()
{
Key = "EntityFilters",
Value = EntityFilters.Attributes
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "RetrieveAsIfPublished",
Value = true
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "LogicalName",
Value = "avobase_tradeorder"
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "MetadataId",
Value = new Guid("00000000-0000-0000-0000-000000000000")
}
}
};
// fire request
IAsyncResult result = OrganizationService.BeginExecute(request, null, OrganizationService);
// wait for response
TaskFactory<OrganizationResponse> tf = new TaskFactory<OrganizationResponse>();
OrganizationResponse response = await tf.FromAsync(
OrganizationService.BeginExecute(request, null, null), iar => OrganizationService.EndExecute(result));
// parse response
EntityMetadata entities = (EntityMetadata)response["EntityMetadata"];
return entities.Attributes.Select(attr => attr.LogicalName).ToList();
}
Edit:
I can create and execute unit tests with Resharper and AgUnit. Thus, the problem is not how to write a unit test in general.
I have tweaked the GetSoapService from the standard Microsoft SDK to accept a fall back value. This means no codes changes are needed when debugging in visual studio and running in CRM. Anyway here it is
public static IOrganizationService GetSoapService(string FallbackValue = null)
{
Uri serviceUrl = new Uri(GetServerBaseUrl(FallbackValue)+ "/XRMServices/2011/Organization.svc/web");
BasicHttpBinding binding = new BasicHttpBinding(Uri.UriSchemeHttps == serviceUrl.Scheme
? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.TransportCredentialOnly);
binding.MaxReceivedMessageSize = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.SendTimeout = TimeSpan.FromMinutes(20);
IOrganizationService ser =new OrganizationServiceClient(binding, new EndpointAddress(serviceUrl));
return ser;
}
public static string GetServerBaseUrl(string FallbackValue = null)
{
try
{
string serverUrl = (string)GetContext().Invoke("getClientUrl");
//Remove the trailing forwards slash returned by CRM Online
//So that it is always consistent with CRM On Premises
if (serverUrl.EndsWith("/"))
{
serverUrl = serverUrl.Substring(0, serverUrl.Length - 1);
}
return serverUrl;
}
catch
{
//Try the old getServerUrl
try
{
string serverUrl = (string)GetContext().Invoke("getServerUrl");
//Remove the trailing forwards slash returned by CRM Online
//So that it is always consistent with CRM On Premises
if (serverUrl.EndsWith("/"))
{
serverUrl = serverUrl.Substring(0, serverUrl.Length - 1);
}
return serverUrl;
}
catch
{
return FallbackValue;
}
}