Chained Where causing Null Reference Exception - c#

I have a problem with Entity Framework Plus. Ctx works, ctx2 works but when I execute ctx3 I get : System.NullReferenceException: Object reference not set to an instance of an object. Any ideas what I might be doing wrong ? GetQueryableContext has been reduced to _context.Set.
Thanks.
var ctx = await GetQueryableContext(request);
ctx = ctx.Where(x => x.DepotNo >= request.DepotNo);
ctx.Update(x => new Order() { LorryLoadingListPrinted = 1111 });
var ctx2 = await GetQueryableContext(request);
ctx2 = ctx2.Where(x => x.DepotNo <= request.DepotNo);
ctx2.Update(x => new Order() { LorryLoadingListPrinted = 2222 });
var ctx3 = await GetQueryableContext(request);
ctx3 = ctx3.Where(x => x.DepotNo >= request.DepotNo).Where(x => x.DepotNo <= request.DepotNo);
ctx3.Update(x => new Order() { LorryLoadingListPrinted = 3333 });

Related

Push multiple objects with dynamic data into a DTO and return it

So I'm trying to use a DTO to reshape and return data, it's not working because I'm trying to push in an array of objects (as an IQueryable - which I don't think works) into the DTO, I'm also trying to push in dynamic data into one of the properties, as seen below in the 'hasCurrentUserLiked' property. I need to figure out How to change the objects from IQueryable, into actual objects so they can all be pushed into the DTO and the dynamic data can be worked out.
This is the code
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
// this doesn't work because 'feeds' is an IQueryable, not an object
var like = await hasCurrentUserLiked(user.Id, feeds.id);
// this has the same problem as above
var feedsToReturn = new FeedsForReturnDto
{
Id = feeds.Id,
PhotoUrl = feeds.photoUrl,
Username = feeds.Username,
Description = feeds.Description,
DateAdded = feeds.DateAdded,
IsImage = feeds.IsImage,
hasCurrentUserLiked = like,
Likes = feeds.Likes
}
return await PagedList<UserPhoto>.CreateAsync(feedsToReturn, userParams.PageNumber, userParams.PageSize);
}
I thought that I might be able to get each image.id in a similar way the 'followerIds' are worked out but I couldn't figure out how to get this to work
[EDIT]
As per Enas Osamas answer, I've changed the code to this and I've debugged it, feedsToReturn has the correct info, so it is doing what its supposed to do. The problem I'm having now is that I'm unable to return it as it can't convert the IEnumerable to an IQueryable. I tried adding an explicit cast but that didn't work, I also tried removing the PagedList, and replacing the type to but this didn't work. My is an IQueryable which might be the problem, I tried changing that to an IEnumerable but this would mess up the code in other places.
This is my new code (still returning 'feeds' instead of 'feedsToReturn', will change when it works)
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<UserPhoto>.CreateAsync(feeds, userParams.PageNumber, userParams.PageSize);
}
I also tried changing some of the types around and this is what I came up with. The problem here is this:
'System.Collections.Generic.IEnumerable<cartalk.api.Dtos.feeds.FeedsForReturnDto>' to 'System.Linq.IQueryable<System.Collections.IEnumerable>'
and this problem is with the 'feedsToReturn' in the last line
public async Task<PagedList<IEnumerable>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<IEnumerable>.CreateAsync(feedToReturn, userParams.PageNumber, userParams.PageSize);
}
PagedList code
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source,
int pageNumber, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
[EDIT]
This is the hasCurrentUserLiked function, it works here
public bool checkCurrentUserLiked(int currentUserId, int imageId)
{
var doesCurrentUserLike = _context.PhotoLikes.Where(x => x.LikerId == currentUserId && x.ImageId == imageId);
var value = true;
if (doesCurrentUserLike == null)
{
value = false;
}
else
{
value = true;
}
return value;
}
You can try something like
feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.id),
Likes = feed.Likes
});
This would return an IEnumerable containing all the feeds mapped to your DTO after being enumerated to avoid performing operations against the database
[EDIT]
you can maybe do a left join with the _context.PhotoLikes.
Like this
feeds.GroupJoin(_context.PhotoLikes,
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
(f, p) => new { feed = f, photoLike = p })
.SelectMany(f => f.photoLike.DefaultIfEmpty(),
(f, p) => new FeedsForReturnDto
{
Id = f.feed.Id,
PhotoUrl = f.feed.photoUrl,
Username = f.feed.Username,
Description = f.feed.Description,
DateAdded = f.feed.DateAdded,
IsImage = f.feed.IsImage,
hasCurrentUserLiked = p != null,
Likes = feed.Likes
});
Just note that in this part
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
the datatypes of properties in both objects must match or it won't compile

How to solve the error A second operation started on this context before a previous operation completed

I'm implementing asp.net core project. In my controller class, create method, At first I insert a record in table APIApplicantHistory as a log history and then I do some calculation to get a value and after that I return back to the create method and and create a new record and insert it into APIApplicant table. For doing that, I wrote some code like below:
namespace CSDDashboard.Controllers
{
public class ApiapplicantsController : Controller
{
private readonly CSSDDashboardContext _context;
public ApiapplicantsController(CSSDDashboardContext context)
{
_context = context;
}
public async Task<IActionResult> Create([Bind("Id,ApiRequestDate,ApiRequestNo,Apiid,ApplicantId,GateId,LastRequestStatus,NocRequestDate,NocRequestNo,Url,Description,IsDeleted")] Apiapplicant apiapplicant)
{
ApiApplicantDto dto = new ApiApplicantDto();
if (ModelState.IsValid)
{
// Apiapplicant myapiapplicant = _context.Apiapplicant.LastOrDefault<Apiapplicant>();
Apiapplicant myapiapplicant = _context.Apiapplicant.ToArray().Last();
int temp = myapiapplicant.Id + 1;
int calcApiApplicantHistoryId = APIApplicantHistoryLog(temp);
var api = _context.Api.Single(p => p.Id == apiapplicant.Apiid);
var gate = _context.Gate.Single(p => p.Id == apiapplicant.GateId);
var applicant = _context.Applicant.Single(p => p.ApplicantId == apiapplicant.ApplicantId);
//var apiapplicanthistory = _context.ApiApplicantHistory.Single(p => p.Id == apiapplicant.LastRequestStatus);
dto.apiId = api.Id;
dto.gateId = gate.Id;
dto.applicantId = applicant.ApplicantId;
using (var _context = new CSSDDashboardContext())
{
_context.Set<Apiapplicant>().Add(new Apiapplicant
{
Apiid = dto.apiId,
GateId = dto.gateId,
ApplicantId = dto.applicantId,
LastRequestStatus = calcApiApplicantHistoryId,
ApiRequestDate = apiapplicant.ApiRequestDate,
ApiRequestNo = apiapplicant.ApiRequestNo,
NocRequestDate = apiapplicant.NocRequestDate,
NocRequestNo = apiapplicant.NocRequestNo,
Url = apiapplicant.Url,
Description = apiapplicant.Description,
IsDeleted = false
});
await _context.SaveChangesAsync();
};
return RedirectToAction(nameof(Index));
}
ViewData["Apiid"] = new SelectList(_context.Api, "Id", "Name", apiapplicant.Apiid);
ViewData["ApplicantId"] = new SelectList(_context.Applicant, "ApplicantId", "Name", apiapplicant.ApplicantId);
ViewData["GateId"] = new SelectList(_context.Gate, "Id", "Name", apiapplicant.GateId);
ViewData["sentResponseType"] = new SelectList(_context.EntityType.Where(g => g.EntityKey=="sentResponseType").ToList(), "ID", "name", apiapplicant.LastRequestStatusNavigation.SentResponseType);
ViewData["unconfirmedReason"] = new SelectList(_context.EntityType.Where(g => g.EntityKey == "unconfirmedReason").ToList(), "Id", "name", apiapplicant.LastRequestStatusNavigation.UnconfirmedReason);
ViewData["lastReqStatus"] = new SelectList(_context.EntityType.Where(g => g.EntityKey == "lastRequestStatus").ToList(), "Id", "name", apiapplicant.LastRequestStatusNavigation.LastReqStatus);
return View(apiapplicant);
}
private int APIApplicantHistoryLog(int myAPIApplicantId)
{
APIApplicantHistoryDto mydto = new APIApplicantHistoryDto();
// var apiapplicantt = _context.ApiApplicantHistory.Single(p => p.ApiApplicantId == myAPIApplicantId);
// mydto.APIApplicantHistoryId = apiapplicantt.Id;
using (var _context = new CSSDDashboardContext())
// using (var transaction = _context.Database.BeginTransaction())
{
_context.Set<ApiApplicantHistory>().Add(new ApiApplicantHistory
{
// ApiApplicantId = mydto.APIApplicantHistoryId,
ApiApplicantId = myAPIApplicantId,
Date = null,
SentResponseType = 0,
UnconfirmedReason = 0,
LastReqStatus = 0,
Description = "",
IdDeleted = false // This field should be totally omited from SQL server
}) ;
_context.SaveChangesAsync();
var myArrayList = new List<int>();
var result = from x in _context.ApiApplicantHistory where x.ApiApplicantId == myAPIApplicantId
select new { x.Id };
foreach (var i in result)
myArrayList.Add(i.Id);
return myArrayList.Max(); ;
}
}
}
My problem is, after running the application and create a new record in APIApplicant, the below error is shown to me:
An unhandled exception occurred while processing the request.
InvalidOperationException: A second operation started on this context
before a previous operation completed. This is usually caused by
different threads using the same instance of DbContext. For more
information on how to avoid threading issues with DbContext, see
https://go.microsoft.com/fwlink/?linkid=2097913.
I appreciate if any one tells me where do I make a mistake?
If you click on the link from the exception message: https://go.microsoft.com/fwlink/?linkid=2097913 it seems straight forward:
Entity Framework Core does not support multiple parallel operations
being run on the same DbContext instance. This includes both parallel
execution of async queries and any explicit concurrent use from
multiple threads. Therefore, always await async calls immediately, or
use separate DbContext instances for operations that execute in
parallel.
If you check your APIApplicantHistoryLog method you are not awaiting your _context.SaveChangesAsync(); method call.

Mocking 2 or more DBContext calls to the same entity fails

I've got an MVC ASP.Net app using Entity Framework v6.0 with an Employee's table.
We're using a Code First approach with the standard Create (CRUD) method that has a EF lookup for existing employee's and also an EF lookup for employee CreatedBy/ModifiedBy fields. Trying to create mocks for both (EF object stubs) fails.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "EmployeeID,TeamID,EmployeeADID,FirstName,MiddleName,LastName,EmailAddress,IsAdministrator,IsDeleted")] Employee employee)
{
if (ModelState.IsValid)
{
//Only unique employee IDs
var existingEmployee = db.Employees.FirstOrDefault(o => o.EmployeeADID == employee.EmployeeADID);
if(existingEmployee != null)
{
ViewBag.Error = "Employee ID must be unique, this employee (" + existingEmployee.FullName + ") already exists in the system.";
ViewBag.TeamID = new SelectList(db.Teams, "TeamID", "Name", employee.TeamID);
return View(existingEmployee);
}
SetAuditFields(employee);
db.Employees.Add(employee);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.TeamID = new SelectList(db.Teams, "TeamID", "Name", employee.TeamID);
return View(employee);
}
The problem is the SetAuditFields call and I need to mock db.Employees.AsNoTracking AsNoTracking for the Edit operation.
private void SetAuditFields(Employee employee, bool onlyModified = false)
{
char sep = '\\';
string pID = User.Identity.Name.Split(sep)[1].ToUpper();
var users = db.Employees.AsNoTracking().Where(c => c.EmployeeADID == pID || c.EmployeeID == employee.EmployeeID).ToList();
var currentUser = users.FirstOrDefault(u => u.EmployeeADID == pID);
if (onlyModified)
{
//Notice the AsNoTracking, when you set the db.Entry(object).State = EntityState.Modified; this query wont return anything as its in Modified Mode.
//var originalEmployee = db.Employees.AsNoTracking().FirstOrDefault(o => o.EmployeeID == employee.EmployeeID);
var originalEmployee = users.FirstOrDefault(o => o.EmployeeID == employee.EmployeeID);
employee.CreatedByID = originalEmployee.CreatedByID;
employee.CreatedDate = originalEmployee.CreatedDate;
employee.ModifiedByID = currentUser.EmployeeID;
employee.ModifiedDate = DateTime.Now;
}
else
{
employee.CreatedByID = currentUser.EmployeeID;
employee.CreatedDate = DateTime.Now;
employee.ModifiedByID = currentUser.EmployeeID;
employee.ModifiedDate = DateTime.Now;
}
}
So how do I mock db.Employees.AsNoTracking after initially mocking db.Employees?
The THESE TWO LINES COMMENTED OUT in the code below don't work and fail with:
"The member 'IQueryable.Provider' has not been implemented on type 'DbSet1Proxy' which inherits from 'DbSet1'
I also tried the mockContext.SetupSequence but I need to interchange between with AsNoTracking on and off. Surely there must be something I'm missing?
[TestMethod()]
public void Create_Submit_Test()
{
// Arrange
var employeeDoesntExist = new Employee { EmployeeID = 0, FirstName = "DoesntExist" };
var employeeAdmin = new Employee { EmployeeID=140, FirstName = "Bern", MiddleName = "", LastName = "O", EmployeeADID = "123", EmailAddress = "Bernard.O#a.com", TeamID = 1, IsDeleted = false, IsAdministrator = true };
var employeeNew = new Employee { FirstName = "Jez", MiddleName = "", LastName = "T", EmployeeADID = "321", EmailAddress = "Jeremy.Thompson#a.com", TeamID = 1, IsDeleted = false, IsAdministrator = true };
var mockContext = new Mock<ICTEntities>();
var employeeEmptyMock = base.GetQueryableMockDbSet(employeeDoesntExist);
var employeeAdminMock = base.GetQueryableMockDbSet(employeeAdmin);
//THESE TWO LINES COMMENTED OUT
//mockContext.Setup(m => m.Employees).Returns(employeeEmptyMock);
//mockContext.Setup(m => m.Employees.AsNoTracking()).Returns(employeeAdminMock);
mockContext.SetupSequence(x => x.Employees.AsNoTracking())
.Returns(employeeEmptyMock)
.Returns(employeeAdminMock);
//I dont want to save it to the Database, otherwise next time we run this the object will already exist, so I mock the call
mockContext.Setup(d => d.SaveChanges()).Returns(1);
var controller = new EmployeesController(mockContext.Object);
controller.ControllerContext = base.MockAccess().Object;
// Act
RedirectToRouteResult result = controller.Create(employeeNew) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result);
Assert.AreEqual("Index", result.RouteValues["Action"]);
}
Here is the GetQueryableMockDbSet method:
protected DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
return dbSet.Object;
}
I ended up creating a chain of mocks using a counter as per https://stackoverflow.com/a/14368486/495455
int callCounter = 1;
mockContext.Setup(m => m.Employees)
.Returns(() =>
{
if (callCounter == 1)
{
callCounter++;
return employeeToEditMockCU;
}
else
{
return employeeMockCU;
}
});
Mocking using a SetupSequence doesn't work for me after the first mock. The db.Employee becomes null after the first call. So I dont use a SetupSequence:
mockContext.SetupSequence(x => x.Employees)
.Returns(employeeToEditMockCU)
.Returns(employeeMockCU);
Also to get around the AsNoTracking() I ended up fetching the record to update and saving it without using EntityState.Modified:
EF Update using EntityState.Modified
How to update record using Entity Framework 6?

Moq setting up variables inside a method

I have the following method that I am trying to test, but my variables are null even if I try to set them up.
public void Cancel(Guid id)
{
var order = _orderRepository.Find(o => o.Id == id); ** This never gets set, even with the setup below.**
if (order == null) return; ** Test Fails here. Returns and all assertions fails.**
order.Status = OrderStatus.Cancelled;
_orderRepository.Update(order);
}
[SetUp]
public void Setup()
{
_orderRepositoryMock = new Mock<IRepository<Order>>();
_accountServiceMock = new Mock<IAccountService>();
_orderService = new OrderService(_accountServiceMock.Object, _orderRepositoryMock.Object);
order = new Order()
{
Id = Guid.NewGuid(),
Customer= new ApplicationUser()
{
Id = Guid.NewGuid().ToString(),
Email = "test#test.com",
FirstName = "Tester",
LastName = "Test",
Address = "123 45 Ave",
City = "ABCVille",
PhoneNumber = "1-888-888-8888",
PostalCode = "T3J 0A4",
Province = "Super"
},
OrderAddons = new List<OrderAddon>(),
Total = 363.99m,
Status = OrderStatus.Created
};
}
[Test]
public void CancelShouldCallRepositoryWhenValid()
{
//var order ... (test data, in setUp)
var id = Guid.NewGuid();
order.Id = id;
// Repository Setup
_orderRepositoryMock.Setup(x => x.Find(o => o.Id == id)).Returns(order);
var wasOrderStatusUpdatedCorrectly = false;
_orderRepositoryMock.Setup(x => x.Update(order))
.Callback((Order o) =>
{
wasOrderStatusUpdatedCorrectly = o.Status == OrderStatus.Cancelled;
});
// Test Service
_orderService.Cancel(id);
// Test Assertions
_orderRepositoryMock.Verify(x => x.Find(o => o.Id == It.IsAny<Guid>()), Times.Once);
_orderRepositoryMock.Verify(x => x.Update(order), Times.Once);
}
Is there anyway to test "var order" ? I tried SetupGet as well and didn't seem to work, Moq is new to me so forgive me in advance if this is something simple and easy.
I think the problem is the Expression that the repository's Find method expects. Try this instead:
_orderRepositoryMock
.Setup(x => x.Find(It.IsAny<Expression<Func<Order, bool>>>()))
.Returns(order);
I'm just guessing at the type parameter for the Expression<>, but hopefully that helps.

Using EF to return specific formatted JSON from ASP.NET MVC

I have a model like below where PermissionGroup has 1-Many relationship with Permission and Permission and Roles have Many-Many relationship.
PermissionGroup 1-----* Permission *--------* Role
I have to be able to return JSON in the following format:
PermissionGroupId : 1,
PermissionGroupName : "Market",
Permissions : [
{PermissionId : 1, PermissionName : "Create"},
{PermissionId : 2, PermissionName : "Update"}
]
That will be asked for a specific RoleId.
Problem:
Since, PermissionGroup has no relation directly with Role so I cannot do a Where linq query. I could do the following but it is not returning desired result like above.
public JsonResult GetRolePermission(int roleid)
{
var list = con.PermissionGroups.Select(pg => new
{
PermissionGroupId = pg.PermissionGroupId,
PermissionGroupName = pg.PermissionGroupName,
Permissions = pg.Permissons.Select(pe => new
{
PermissionId = pe.PermissionId,
PermissionName = pe.PermissionName
})
})//Maybe do the where query here somehow like where(f => f.RoleId == roleid);
return Json(list);
}
Any suggestion is appreciated.
You can use Any method in Where? Like this:
var list = con.PermissionGroups
.Where(pg => pg.Permissons.Any(p => p.Roles.Any(r => r.RoleId == roleId)))
.Select(pg => new
{
PermissionGroupId = pg.PermissionGroupId,
PermissionGroupName = pg.PermissionGroupName,
Permissions = pg.Permissons.Select(pe => new
{
PermissionId = pe.PermissionId,
PermissionName = pe.PermissionName
})
});
I found a way to do this but highly doubt that it is efficient.
var getPermissionsForRole = context.Roles.Where(roleid => roleid.RoleId == 1).Select(p => p.Permissions).ToList();
var getAllPermissionGroup = context.PermissionGroups.ToList();
List<PermissionGroup> jsonPg = new List<PermissionGroup>();
foreach (var pg in getAllPermissionGroup)
{
PermissionGroup newPg = new PermissionGroup();
newPg.PermissionGroupId = pg.PermissionGroupId;
newPg.PermissionGroupName = pg.PermissionGroupName;
newPg.Permissons = new List<Permission>();
foreach (var pers in getPermissionsForRole[0])
{
if (pers.PermissionGroup.PermissionGroupId == pg.PermissionGroupId)
{
Permission newPe = new Permission();
newPe.PermissionId = pers.PermissionId;
newPe.PermissionName = pers.PermissionName;
newPg.Permissons.Add(newPe);
}
}
if (newPg.Permissons.Count > 0)
{
jsonPg.Add(newPg);
}
}
var json = JsonConvert.SerializeObject(jsonPg);
Still open to better code.

Categories