ABP UnitOfWork is not locking database record for IsolationLevel RepeatableRead - aspnetboilerplate

It's better if this issue is explained with an example. I have a database table Person with an int column named [Num]. It has only a record with the initial value of Num == 0.
In my PersonAppService.cs, there are the following 2 methods
public void TestIncrementA()
{
using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions { IsolationLevel = IsolationLevel.RepeatableRead })
{
var person = _personRepository.Get(1);
person.Num += 1;
Thread.Sleep(3000);
uow.Complete();
}
}
public void TestIncrementB()
{
using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions { IsolationLevel = IsolationLevel.RepeatableRead })
{
var person = _personRepository.Get(1);
person.Num += 1;
uow.Complete();
}
}
The 2 methods are essentially the same which increment the value of the column Num by one except that the first method delays the thread.
Now in the console of a web browser, I run the following commands in quick succession.
abp.services.app.person.testIncrementA();
abp.services.app.person.testIncrementB();
I would expect the value of Num in my database to be 2 now since it's been incremented twice. However it's only 1.
It's clear the RepeatableRead UoW is not locking the row properly. I have also tried using the attribute [UnitOfWork(IsolationLevel.RepeatableRead)] to no avail.
But, if I were to set the following in the PreInitialize of a module, it works.
Configuration.UnitOfWork.IsolationLevel = IsolationLevel.RepeatableRead;
This will unfortunately force RepeatableRead app-wide. Is there something that I'm overlooking?

To set a different isolation level from the ambient unit of work, begin another with RequiresNew:
using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions
{
Scope = TransactionScopeOption.RequiresNew, // Add this
IsolationLevel = IsolationLevel.RepeatableRead
})
{
...
}
Explanation
From https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work:
If a unit of work method calls another unit of work method, both use the same connection & transaction. The first entered method manages the connection & transaction and then the others reuse it.
The default IsolationLevel for a unit of work is ReadUncommitted if it is not configured. ...
Conventional Unit Of Work Methods
Some methods are unit of work methods by default:
...
All Application Service methods.
...

Related

ObjectDisposedException during tests

READ EDIT
I have a similar implementation to AsyncCrudAppService related to filtering queries. When I run tests on top of ABPs implementation of Application Services derived of AsyncCrudAppServiceBase, everything runs fine. When I do the same running on top of my custom "filters", I get the following error:
System.ObjectDisposedException : Cannot access a disposed object [...]
Object name: 'DataManagerDbContext'.
I know the solution is using IUnitOfWorkManager and calling Begin() method to define a UnitOfWork, but since I am working with AppServices, I thought there was already a UnitOfWork defined. These are my methods:
public PagedResultDto<StateDetails> GetEditorList(EditorRequestDto input)
{
var query = _stateRepository.GetAllIncluding(p => p.Country).AsQueryable();
query = ApplySupervisorFilter(query);
query = query.ApplyFiltering(input, "Name");
var totalCount = query.Count();
query = query.ApplySorting<State, int, PagedAndSortedResultRequestDto>(input);
query = query.ApplyPaging<State, int, PagedAndSortedResultRequestDto>(input);
var entities = query.ToList();
return new PagedResultDto<StateDetails>(totalCount, ObjectMapper.Map<List<StateDetails>>(entities));
}
private IQueryable<State> ApplySupervisorFilter(IQueryable<State> query)
{
if (!SettingManager.GetSettingValue<bool>(AppSettingNames.SupervisorFlag))
{
query = ApplyUncategorizedFilter(query);
}
return query;
}
private IQueryable<State> ApplyUncategorizedFilter(IQueryable<State> query)
{
return query.Where(
p => !p.CountryId.HasValue);
}
My passing test (with manual UnitOfWork):
[Fact]
public async Task GetEditorListWithouSupervisorFlag_Test()
{
using (UnitOfWorkManager.Begin())
{
await ChangeSupervisorFlag(false);
var result = _stateAppService.GetEditorList(
new EditorRequestDto
{
MaxResultCount = 10,
});
result.Items.Any(p => p.Country == null).ShouldBe(true);
}
}
Does anybody know an solution to this "issue"? It would be annoying to define a UnitOfWork for every test I perform. It also seems like I am doing something wrong
EDIT
I have solved the issue. I must use an interface for my Application Service when running tests so it is able to mock it properly
I have solved the issue. I must use an interface for my Application Service when running tests so it is able to mock it properly

Entity Framework struggling to recover after Distributed Transaction

Working on a WebApi project that's backed by mssql with EntityFramework, and also Oracle (12c) using oracle's ManagedDataAccess.Client.OracleConnection. We use autofac to inject an instance of our context per request, but all oracle access is just done ad hoc.
We have certain operations that depend on both databases at the same time, so we opted to use the TransactionScope object to manage the transaction.
For the most part it works well, the light weight transactions that are promoted to distributed work great. But there is one issue I've encountered after completing a distributed transaction.
Given:
public void Test()
{
var preItem = new HelpItem
{
Field1 = "pre batch";
};
_context.Items.Add(preItem);
_context.SaveChanges(); // This save always works.
var batchResult = FooService.BatchOperation(true);
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // This will succeed/fail depending on whether FooService caused a distributed transaction.
}
With the BatchOperation method as:
public Result BatchOperation(bool triggerDtc)
{
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
if (triggerDtc){
// Make requests to both databases.
} else {
// Make request to one database.
}
// Always complete for the sake of the demonstration.
transaction.Complete();
}
}
If a distributed transaction is encountered and then completed & fully disposed EF doesn't seem to be able to recover and go back to working as it was before the transaction came into play.
The error:
Distributed transaction completed. Either enlist this session in a new
transaction or the NULL transaction.
What would be the correct way to handle this?
For this particular case you can simply create another transaction around the second part:
var batchResult = FooService.BatchOperation(true);
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // This save depends on whether FooService caused a distributed transaction.
transaction.Complete();
}
But this issue came up because the FooService.BatchOperation method was altered with just a lookup to the other database, unknowingly breaking every method out there that continues to use the context after calling it. With normal transaction a single EF context can freely be used in and out of them without issue, is there any way to achieve the same with a distributed transaction?
EDIT:
This really just has me confused now. Just the act of making a request in another (non distributed) transactionscope is enough to restore EF functionality.
public IHttpActionResult Test()
{
var preItem = new HelpItem
{
Field1 = "pre batch";
};
_context.Items.Add(preItem);
_context.SaveChanges(); // This save works.
var batchResult = FooService.BatchOperation(true);
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var lookupAnything = _context.Items.ToList();
transaction.Complete(); // This is optional, because we really don't care and it's disposed either way.
}
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // Now this always works.
}
Obviously I can't just go around putting this everywhere, so still not sure what the actual solution is.

Whether I am writing the correct test method in Unit Test?

I am develeloping MVC application and writing the unit test.
I am confused about the coding pattern/process of unit test.
I am writing the unit test but I am not aware of , whether I am writing in proper way or not.
I am giving one example of the test case please check.
Basically, in test method I am writing the same code which I have written in GetPartiesByEmployee() method and I am comparing the no. of records which return from the method and the records return from code blog in test method is that correct ?
Is that correct ?
[TestMethod]
public void Test_Get_Parties_By_Employee_Method()
{
var actualResult = oPartyHelper.GetPartiesByEmployee(6);
Employee oEmployee = new Employee();
oEmployee = db.Employees.Find(6);
var roles = oEmployee.Roles.ToList();
List<Party> parties = new List<Party>();
foreach (Role item in roles)
{
var PartyCollection = from e in item.Parties.OrderBy(e => e.Name)
where (e.IsDeleted == false || e.IsDeleted == null)
select e;
parties.AddRange(PartyCollection.ToList());
}
parties=parties.Distinct().OrderBy(p => p.Id).ToList();
var expectedCount = parties.Count();
var actualList = (List<Party>)actualResult;
var actualCount = actualList.Count;
Assert.AreEqual(expectedCount, actualCount, "All parties are not same");
}
Actual Method :
public List<Party> GetPartiesByEmployee(int employeeId)
{
Employee oEmployee = new Employee();
oEmployee = db.Employees.Find(employeeId);
var roles = oEmployee.Roles.ToList();
List<Party> parties = new List<Party>();
foreach (Role item in roles)
{
var PartyCollection = from e in item.Parties.OrderBy(e => e.Name)
where (e.IsDeleted == false || e.IsDeleted == null)
select e;
parties.AddRange(PartyCollection.ToList());
}
return parties.Distinct().OrderBy(p=>p.Id).ToList();
}
No, this is not how unit testing works. You don't copy the same code into the test method, you test the actual object which has the code. (Just copying the code would not only create odd and probably unhelpful tests, but it would duplicate everything in the system, which is unmaintainable.)
So let's say you have a method like this:
public int ActualMethod()
{
var x = 0;
var y = 1;
return x + y;
}
You would not test that code by doing this:
[Test]
public void TestMethod()
{
var x = 0;
var y = 1;
Assert.Equal(1, x + y);
}
You should instead have something like this:
[Test]
public void TestMethod()
{
var testableObject = new SomeObject();
var expectedResult = 1;
var actualResult = testableObject.ActualMethod();
Assert.Equal(expectedResult, actualResult);
}
(Which you can modify for readability as you see fit. I was perhaps overly-explicit with the lines of code and variable names in that test method, just to demonstrate what's happening.)
The idea is that the unit tests load the actual module being tested, not copies of the lines of code. Think of it from an encapsulation point of view for object-oriented design. Nothing outside of those objects, including the tests themselves, should know anything about their internal implementations. The tests are just interacting with the objects and validating that the results match what's expected. The tests don't care how those objects internally achieve the results, only that the results meet expectations.
In general, unit tests follow three steps:
Arrange
Act
Assert
That is...
First, you arrange the objects for your test. This might involve resetting some static data into a known state, instantiating an object (or grabbing it from a factory, etc.), setting some properties, etc. Basically you're creating a known state from which a test will be run.
Second, you act upon that object. You perform some action which should change the state of the system in some way. Usually this is just calling a method on the object (or perhaps passing the object to a method somewhere else in some cases). This is what you're testing. That the code which changes the state of the system will change it from one known state to another expected resulting state.
Third, you assert the result of the test. Since you created a known state in the first step, changed the state in the second step, now you observe the resulting state in the third step.
You can use NUnit in several ways. It depends on your code and what your test is all about. In your case yes, comparing totals you would use the AreEqual method. Another common example is if you would like to see if a code generates exceptions or not - maybe to check library updates etc. Something similar to the following might be useful :
[TestCase]
public void TestCase()
{
try
{
// Write you code here that might generate an exception
Assert.AreEqual(true, true);
}
catch (Exception e)
{
Assert.Fail(e.Message, e.GetType().Name);
}
}
As you can see if the executes successfully I use AreEqual, and comparing true with true so it will execute fine. If however the code generates an exception, I'd send back the error messages.

How to call delegate only once / one time with moles?

How is it possible to call a delegated Method only once / one time with moles?
MyClass.AllInstances.ResultateGet = delegate { return new ResultatInfoCollection(); };
I want to call the Method "ResultateGet" only one time because the init is quite complex the first time without a delegate.
target.UpdateResultate(); //calls delegate "ResultateGet"
//Assert some stuff
target.Verify(); //needs original function "ResultateGet" so unit test is useful
I am generally interested how to call a moles delegate one time ore a specific number of times before the original function is called and not the delegate.
Update:
I found a way, that seems a little bit cumbersome. Any better Solution?
ResultatInfoCollection x = new ResultatInfoCollection();
MolesContext.ExecuteWithoutMoles(() => x = target.Resultate);
Also, see my answer to: How to assign/opt from multiple delegates for a 'moled' method? This provides an example of gating logic inside the anonymous method.
Ooh, good question! I have encountered this, myself. What you are looking for is called a "fallthrough" behavior (execution of the original code). The anonymous method to which Moles detours must contain a switching mechanism that falls through, after the first call. Unfortunately, I don't believe a fallthrough feature is included in Moles, at this time.
Your updated workaround is exactly what you need -- calling fallthrough would do the same thing. I suggest adding a sentinel value, doFallthrough, that gates the calls:
bool doFallthrough = false;
ResultatInfoCollection x = new ResultatInfoCollection();
MyClass.AllInstances.ResultateGet = delegate {
if (!doFallthrough)
{
doFallthrough = true;
return new ResultatInfoCollection();
}
MolesContext.ExecuteWithoutMoles(() => x = target.Resultate);
};
Calling a specific number of times simply requires a change to the sentinel value type:
int doFallthrough = 0;
ResultatInfoCollection x = new ResultatInfoCollection();
MyClass.AllInstances.ResultateGet = delegate {
if (++doFallthrough < 5)
return new ResultatInfoCollection();
MolesContext.ExecuteWithoutMoles(() => x = target.Resultate);
};
Old question, but since I found it when I was searching, I'll answer it for the next person with my solution.
Using MolesContext.ExecuteWithoutMoles to call the original function works just fine in most cases, however, if you are moling any other functions or classes downstream from this call, they won't be moled, either.
Given the following class:
public class TheClass
{
public int TheFunction(int input){
return input + TheOtherFunction();
}
public int TheOtherFunction(){
return DateTime.Now.Minutes;
}
}
If you use the MolesContext.ExecuteWithoutMoles approach:
MTheClass.AllInstances.TheOtherFunctionInt = (instance) => {
return 5;
};
MTheClass.AllInstances.TheFunctionInt = (instance, input) =>
{
//do your stuff here, for example:
Debug.WriteLine(input.ToString());
var result = MolesContext.ExecuteWithoutMoles<int>(() => instance.TheFunction(input));
//do more stuff, if desired
return result;
};
Your mole for OtherFunction will not be hit, because it was (indirectly) executed within the "without moles" scope.
However, you can add and remove moles delegates at any time, so that allows you to do the following, as outlined in the Moles Documentation (p. 24)
MTheClass.AllInstances.TheOtherFunctionInt = (instance) => {
return 5;
};
MolesDelegates.Func<TheClass, int, int> molesDelegate = null;
molesDelegate = (instance, input) =>
{
//do your stuff here, for example:
Debug.WriteLine(input.ToString());
int result = 0;
try{
MTheClass.AllInstances.TheFunctionInt = null;
result = instance.TheFunction(input);
}
finally{
MTheClass.AllInstances.TheFunctionInt = molesDelegate;
}
//do more stuff, if desired
return result;
};
MTheClass.AllInstances.TheFunctionInt = molesDelegate;
The OtherFunction moles is still hit. With this method, you can remove moling just from the specific method without impacting your other moles. I've used this, and it works. The only trouble I can see is that it won't work if you have a recursive function, or possibly a multi-threaded situation.

Entity Framework Optimistic Concurrency Exception not occuring

We have an ASP.Net MVC application that uses EF4 as its data access layer and we're seeing unexpected behaviour with regards to OptimisitcConcurrencyExceptions not being thrown when we think they should be.
We have simplified the problem down to the following code...
using System.Linq;
using Project.Model;
namespace OptimisticConcurrency
{
class Program
{
static void Main()
{
Contact firstContact = null;
using (var firstEntities = new ProjectEntities())
{
firstContact = (from c in firstEntities.Contacts
where c.LastName == "smith" select c).Single();
}
using (var secondEntities = new ProjectEntities())
{
var secondContact = (from c in secondEntities.Contacts
where c.LastName == "smith" select c).Single();
secondContact.Title = "a";
secondEntities.SaveChanges();
}
firstContact.Title = "b";
using (var thirdEntities = new ProjectEntities())
{
var thirdContact = (from c in thirdEntities.Contacts
where c.LastName == "smith" select c).Single();
thirdContact.Title = firstContact.Title;
//EXPLICITLY SET VERSION HERE
thirdContact.Version = firstContact.Version;
thirdEntities.SaveChanges();
}
}
}
}
This is a rather simple version of what happens in our MVC app, but the same problem occurs.
When we call SaveChanges on the thirdEntities, I expect the exception and nothing is being thrown.
Much more interestingly, when we attach the SQL Profiler, we see that the Version is being used in the where clause but it is thirdEntities Version value (the current one in the DB) being used, not the firstEntities values DESPITE it being explicitly set immediately before SaveChanges is called. SaveChanges is resetting the Version to be the retrieved value not the set value.
In the EDMX, the Version is set to have a StoreGeneratedPattern is set to Computed.
Anyone have any idea what is going on here?
This is a problem. Once the column is set to Computed you can't set its value in the application (you can but the value is not used).
Edit:
If you load entity from database it is by default tracked with the context. The context stores its original values. Original values are for example used for snapshot change tracking but they are also used as the only valid source of Computed properties. If you set Computed property in your entity the value is not used and original value is used insted. The workaround is to modify original value (before you modify anything else):
using (var context = new TestEntities())
{
var entityToUpdate = context.MyEntities.Single(e => e.Id == someId);
entityToUpdate.Timestamp = entity.Timestamp;
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entityToUpdate);
entry.ApplyOriginalValues(entityToUpdate);
// set modified properties
context.SaveChanges();
}
Edit 2:
Btw. once you have both actually loaded timestamp and previously retrieved timestamp you can simply compare them in your application instead of doing it in the database.

Resources