Entity Framework Core: New transaction is not allowed because there are other threads running in the session - asp.net-core

I have a database with a hierarchy of categories. Each category has a parentcategoryid. I call the following function to load the top level categories and then it recursively calls itself to load all the children.
However, I get the following error:
SqlException: New transaction is not allowed because there are other
threads running in the session.
public async Task LoadCategoriesAsync()
{
await LoadCategoriesByParentId(null);
}
private async Task LoadCategoriesByParentId(int? sourceParentId, int? parentId)
{
var sourceCategories = _dbContext.SourceCategory.Where(c => c.ParentCategoryId == sourceParentId);
foreach (var sourceCategory in sourceCategories)
{
var newCategory = new Category()
{
Name = sourceCategory.Name,
Description = sourceCategory.Description,
ParentCategoryId = parentId
};
_dbContext.Category.Add(newCategory);
await _dbContext.SaveChangesAsync();
//category.EntityId = newCategory.Id;
//_dbContext.SourceCategory.Update(category);
//await _dbContext.SaveChangesAsync();
await LoadCategoriesByParentId(sourceCategory.CategoryId, newCategory.Id);
}
}

Your Where() statement doesn't retrieve the data; just "opens the cursor" (in old-speak). So, you can't do SaveChange(). The simplest solution is to convert IEnumerable to List or Array:
var rootCategories = _dbContext.SourceCategory.Where(c => c.ParentCategoryId == parentId).ToList();
But I would strongly recommend you google the error and understand why it is happening. To do this recursively is begging for trouble

Related

How to fill a Mock<DbSet> in Nunit with Moq?

I am trying to fill a mock dbset with a testentity but I only get exception after exception. I am very new to Nunit testing and haven't really got the hang of it. I am trying to test a simple delete method in my repository, here is the code for the method:
public async Task DeleteQuestion(Question questionToRemove)
{
if (questionToRemove is not null)
{
var questionsAnswers = await _context.Answers.Where(a => a.Question == questionToRemove).ToListAsync();
foreach (Answer a in questionsAnswers)
{
_context.Answers.Remove(a);
await _context.SaveChangesAsync();
}
_context.Questions.Remove(questionToRemove);
await _context.SaveChangesAsync();
var questions = await _context.Questions.Where(q => q.Quiz == questionToRemove.Quiz).ToListAsync();
int order = 1;
foreach (Question question in questions)
{
question.QuestionOrderId = order;
await _context.SaveChangesAsync();
order++;
}
}
}
And here is the test that I have written:
public async Task DeleteQuestionFunction()
{
//Arrange
Question testQuestion = new Question { QuestionId = 1, QuestionText = "Test" };
Answer testAnswer = new Answer { Question = testQuestion, AnswerId = 1, AnswerText = "TestAnswer" };
_appDBContextMock.SetupAdd(x => x.Questions.Add(testQuestion));
_appDBContextMock.SetupAdd(x => x.Answers.Add(testAnswer));
//Act
var questionListcomponent = new QuestionListComponent();
questionListcomponent.DeleteQuestion(testQuestion);
var testresult = await _questionRepositoryMock.Object.GetQuestionById(testQuestion.QuestionId);
//Assert
Assert.That(testresult, Is.Null);
}
When I only used the Setup method I got this exception:
System.NotSupportedException : Unsupported expression: x =>
x.Questions Non-overridable members (here: AppDbContext.get_Questions)
may not be used in setup / verification expressions.
and right now with the SetupAdd function I get this exception:
System.ArgumentException : Can not instantiate proxy of class:
TietoQuiz.Models.DbContexts.AppDbContext. Could not find a
parameterless constructor. (Parameter 'constructorArguments')
I think my problem is that I don't know how to set up a mock dbset properly. I tried to Google, but I found so many different ways and so many comments saying "that is not recommended/ the wrong way"! It is very confusing for a newbie, so I hope someone here can help!
If you want to write a test by using a context, you must create your own context and verify the data after running it. You cannot use mocks with non overridable methods.
You must use a temporary file to create this context not to change your production database.

EF Core not setting class variables automatically

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 .

use entity frame work core in multy thread

i use async funcs with await Task.WhenAll inmy function.
and some times i get exception with this message "A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext".
public async Task<int> getCountAsync(long userId)
{
return await _appDbContenxt.onTimeRequests
.Where(i => (userId == 0 ? true : i.userId == userId))
.CountAsync()
;
}
public async Task<List<OnTimeRequest>> GetOnTimeRequestsAsync(int pageSize, int currentPage, long userId)
{
return await _appDbContenxt.onTimeRequests
.Where(i => (userId == 0 ? true : i.userId == userId))
.OrderByDescending(i => i.id)
.Skip((currentPage - 1) * pageSize)
.Take(pageSize)
.ToListAsync()
;
}
public async Task<OnTimePaginationDto> getUserOnTimeRequests(int pageSize, int currentPage, long userId)
{
Task<int> count = _onTimeRequestsRepository.getCountAsync(userId);
Task<List<OnTimeRequest>> values = _onTimeRequestsRepository.GetOnTimeRequestsAsync(pageSize, currentPage, userId);
await Task.WhenAll(count, values);
OnTimePaginationDto onTimePaginationDto = new OnTimePaginationDto
{
count = count.Result,
values = _mapper.Map<IList<ReadOnTimeRequestDto>>(values.Result)
};
return onTimePaginationDto;
}
this is my functions. getUserOnTimeRequests in on timeRequestService.
getCountAsync and GetOnTimeRequestsAsync in timeRequestRepository .
and this is my startup code
services.AddScoped<IMemberSheetRepository, MemberShiptRepository>();
services.AddScoped<IPackageHistoryRepository, PackageHistoryRepository>();
services.AddScoped<IOnTimeRequestsRepository, OnTimeRequestsRepository>();
services.AddScoped<IMemberShipService, MemberShipService>();
services.AddScoped<IPackageHistoryService, PackageHistoryService>();
services.AddScoped<IOnTimeRequestService, OnTimeRequestService>();
services.AddDbContext<AppDbContext>(options =>
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.UseSqlServer(Configuration.GetConnectionString("DbConnection")),ServiceLifetime.Transient
);
thanks for your helps.
only when efcore send the SQL command to your database that your LINQ commands
has these at the end of you commands like(.tolist or .count or ...)
that means LINQ query doesn't load (if you check that type, it is IQueryable), and when you ask it for data by .tolist or .count or ... it will be
sent the SQL commands to the database and many of LINQ commands like .remove
not async and you cannot use them as async

breeze.js not honoring the "noTracking" option when end point returns multiple result sets

Consider this breze query:
return EntityQuery.from('myAPI')
.noTracking(true)
.using(manager).execute()
.then(querySucceeded)
.fail(queryFailed);
My API is defined like this:
[HttpGet]
public object myAPI()
{
// var userId = get the users id from auth ticket
var userPref = _contextProvider.Context.UserPreferences.Where(u => u.userId == userId);
var userOptions = _contextProvider.Context.UserOptions.Where(u => u.userId == userId);
return new
{
userPref,
userOptions
};
}
I know I can get access to the raw data, which is great. But in addition to this, the entities are created in the entity manager, which I would prefer they not be. This works fine for apis that return IQueryable. Is there a different syntax for noTracking for web apis that returns multiple result sets?
thanks
I can't reproduce the error you describe. I have a similar DocCode test that passes which references Breeze v1.5.3.
Here is the pertinent NorthwindController method:
[HttpGet]
public object Lookups()
{
var regions = _repository.Regions;
var territories = _repository.Territories;
var categories = _repository.Categories;
var lookups = new { regions, territories, categories };
return lookups;
}
And here's the passing QUnit test:
asyncTest('object query (e.g., lookups) w/ "no tracking" does not add to cache', function () {
expect(2);
var em = newNorthwindEm();
EntityQuery.from('Lookups')
.noTracking(true)
.using(em).execute()
.then(success).fail(handleFail).fin(start);
function success(data) {
var lookups = data.results[0];
var hasLookups = lookups &&
lookups.categories && lookups.regions && lookups.territories;
ok(hasLookups, 'Expected a lookups object w/ categories, regions and territories');
var cached = em.getEntities();
var len = cached.length;
equal(0, len, 'Expected ZERO cached entities of any kind and got ' + len);
}
});
If I comment out the noTracking(true) clause, the test fails and tells me that there are 65 entities in cache ... as predicted.
What am I missing?

EntityFramework, Insert if not exist, otherwise update

I'm having a Entity-Set Countries, reflecting a database table '<'char(2),char(3),nvarchar(50> in my database.
Im having a parser that returns a Country[] array of parsed countries, and is having issues with getting it updated in the right way. What i want is: Take the array of countries, for those countries not already in the database insert them, and those existing update if any fields is different. How can this be done?
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
//Code missing
}
}
I was thinking something like
for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields.
Hope anyone have had this issues before, and have a solution for me. If i cant use the Country object and insert/update an array of them easy, i dont see much benefict of using the framework, as from performers i think its faster to write a custom sql script that inserts them instead of ect checking if an country is already in the database before inserting?
Solution
See answer of post instead.
I added override equals to my country class:
public partial class Country
{
public override bool Equals(object obj)
{
if (obj is Country)
{
var country = obj as Country;
return this.CountryTreeLetter.Equals(country.CountryTreeLetter);
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = hash * 7 + (int)CountryTreeLetter[0];
hash = hash * 7 + (int)CountryTreeLetter[1];
hash = hash * 7 + (int)CountryTreeLetter[2];
return hash;
}
}
and then did:
var data = e.ParsedData as Country[];
using (var db = new entities())
{
foreach (var item in data.Except(db.Countries))
{
db.AddToCountries(item);
}
db.SaveChanges();
}
I would do it straightforward:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
foreach(var country in data)
{
var countryInDb = db.Countries
.Where(c => c.Name == country.Name) // or whatever your key is
.SingleOrDefault();
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
I don't know how often your application must run this or how many countries your world has. But I have the feeling that this is nothing where you must think about sophisticated performance optimizations.
Edit
Alternative approach which would issue only one query:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
var names = data.Select(c => c.Name);
var countriesInDb = db.Countries
.Where(c => names.Contains(c.Name))
.ToList(); // single DB query
foreach(var country in data)
{
var countryInDb = countriesInDb
.SingleOrDefault(c => c.Name == country.Name); // runs in memory
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
The modern form, using later EF versions would be:
context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added);
context.SaveChanges();
AlreadyExists can come from checking the key or by querying the database to see whether the item already exists there.
You can implement your own IEqualityComparer<Country> and pass that to the Except() method. Assuming your Country object has Id and Name properties, one example of that implementation could look like this:
public class CountryComparer : IEqualityComparer<Country>
{
public bool Equals(Country x, Country y)
{
return x.Name.Equals(y.Name) && (x.Id == y.Id);
}
public int GetHashCode(Country obj)
{
return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
}
}
and use it as
data.Countries.Except<Country>(db, new CountryComparer());
Although, in your case it looks like you just need to extract new objects, you can use var newCountries = data.Where(c => c.Id == Guid.Empty); if your Id is Guid.
The best way is to inspect the Country.EntityState property and take actions from there regarding on value (Detached, Modified, Added, etc.)
You need to provide more information on what your data collection contains i.e. are the Country objects retrieved from a database through the entityframework, in which case their context can be tracked, or are you generating them using some other way.
I am not sure this will be the best solution but I think you have to get all countries from DB then check it with your parsed data
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
List<Country> mycountries = db.Countries.ToList();
foreach(var PC in data)
{
if(mycountries.Any( C => C.Name==PC.Name ))
{
var country = mycountries.Any( C => C.Name==PC.Name );
//Update it here
}
else
{
var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters
newcountry.Name = PC.Name;
db.AddToCountries(newcountry)
}
}
db.SaveChanges();
}
}