I am using .NET Core and SQLKata to access SQL Server database.
I have a method to get all records from the database using SQLKata.Execution.PaginationResult.
This is in Repository:
public class MyTableRepository : IMyTableRepository
{
private QueryFactory _db;
public MyTableRepository(IConfiguration configuration)
{
var conn = new
SqlConnection(configuration.GetConnectionString("MyTable"));
_db = new QueryFactory(conn, new SqlKata.Compilers.SqlServerCompiler());
}
public PaginationResult<MyModel> GetAll(int page = 0, int perPage = 25)
{
dbResult = _db.Query("MyTable").Paginate<MyModel>(page, perPage);
return dbResult;
}
The above is called from my Controller like so:
private readonly IMyTableRepository _MyTableRepository;
public MyTableController(IMyTableRepository MyTableRepository)
{
_MyTableRepository = MyTableRepository;
}
[HttpGet]
[Route("GetMyTable")]
public List<MyModel> GetMyTable()
{
PaginationResult<MyModel> dbResult = MyTableRepository.GetAll(1,
25);
List<MyModel> AccumResult = dbResult.List.ToList();
while(dbResult.HasNext)
{
dbResult = dbResult.Next();
AccumResult.AddRange(dbResult.List.ToList());
}
return AccumResult;
}
How do I get the Next set of result from dbResult ?
I tried below, after I execute GetMyTable, I execute GetNextMyTable, but in PaginationResult GetNext(), dbResult is always null.
In controller:
[HttpGet]
[Route("GetNextMyTable")]
public List<MyTable> GetNextMyTable()
{
var result = _MyTableRepository.GetNext().List;
return result.ToList();
}
In Repository:
public PaginationResult<MyTable> GetNext()
{
while(dbResult.HasNext) //--->> dbResult is null
{
dbResult = dbResult.Next();
return dbResult;
}
return null;
}
If I do the Next method inside the Controller, I am also getting an error
private readonly IMyTableRepository _MyTableRepository;
private PaginationResult<SP_SMA_Reporting_Accts> dbResult;
[HttpGet]
[Route("GetMyTable")]
public List<MyModel> GetMyTable()
{
var dbResult = _MyTableRepository.GetAll(1, 25).List;
return dbResult.ToList();
}
[HttpGet]
[Route("GetNextMyTable")]
public List<MyTable> GetNextMyTable()
{
var result = dbResult.Next().List;//->Error since dbResult is null
return result.ToList();
}
In short refactor your repository method GetAll to call the db.Query
use:
_db.Query("MyTable").Paginate<MyModel>(page, perPage);
instead of
_db.Paginate<MyModel>(new Query("MyTable"), page, perPage);
Explanation:
when calling the Next() method the PaginationResult delegate the pagination process to the holding query, in your case since your are passing new Query() thus no connection info, you are getting null.
Related
I have a function which returns an IEnumerable dynamic object
public IEnumerable<dynamic> GetNameByRequestIds(List<long> requestIds) {
return (
from r in Session.All<Request>()
where requestIds.Contains(r.REQ_ID)
select new {
ReqId = r.REQ_ID,
Name = r.Name
}
).AsEnumerable();
}
In the Unit Test, I mock this by
List<dynamic> myList = new List<dynamic>() {
new {
ReqId = 1,
Name = "myName",
}
};
_db.Setup(x => x.GetNameByRequestIds(It.IsAny<List<long>>())).Returns(myList);
But when I executed the following code, it threw out an exception
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''object' does not contain a definition for 'ReqId''
var dynamicList = _db.GetNameByRequestIds(myReqIds).ToList();
foreach (var r in dynamicList ) {
Console.WriteLine(r.ReqId);
Console.WriteLine(r.Name);
}
It works while I query to real DB, but fails in the unit test.
In debug mode
dynamicList, the type is
System.Collections.Generic.List<dynamic> {System.Collections.Generic.List<object>}
and the r shows
Type <Anonymous Type>
What mocking framework are you using? That somewhat looks like Moq, however the code to access the mocked results looks incomplete. I've tried the above with Moq (v16.4) and it worked as expected.
What isn't clear in your example is the Type for _db. A cut down example of what works:
// The DB wrapper (i.e. you're real code to be mocked away)
public interface IData
{
IEnumerable<dynamic> GetNameByRequestIds(IEnumerable<long> ids);
}
public class Data : IData
{
public IEnumerable<dynamic> GetNameByRequestIds(IEnumerable<long> ids)
{
return (
from r in Session.All<Request>()
where requestIds.Contains(r.REQ_ID)
select new {
ReqId = r.REQ_ID,
Name = r.Name
}
).AsEnumerable();
}
}
// Then the code under test... (The service/controller you want to unit test...)
public class Consumer
{
private readonly IData _db;
public Consumer(IData db)
{
_db = db;
}
public void DoSomething(IEnumerable<long> ids)
{
foreach (var row in _db.GetNameByRequestIds(ids)
{
Console.WriteLine(row.ReqId);
Console.WriteLine(row.Name);
// Do whatever with the returned data rows...
}
}
}
// Then the Test setup itself...
[Test]
public void TestConsumerDoesSomething()
{
var mock = new Mock<IData>();
mock.Setup(x => x.GetNameByRequestIds(It.IsAny<IEnumerable<long>>())).Returns(populate());
var obj = new Consumer(mock.Object);
obj.DoSomething();
}
private IEnumerable<dynamic> populate()
{
return new List<dynamic>
{
new {ReqId = 1, Name = "myName"}
};
}
This all appeared to work as you would expect with the method under test ("DoSomething") accessing the data provided by the Mock instead of the "real" DB provider and the mocked dynamic object exposing a ReqId and Name.
I have been getting a null reference error when running my test but cant figure it out. Below is my test
[Test]
[TestCase(...)]
public void Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
using (var _imprintDbContext = new ImprintDbContext(_dbContextOptions))
{
var mockExecRepo = new Mock<IExecutionReportRepository>();
mockExecRepo.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>())).Verifiable();//.Returns<Task<PagedResult<ExecutionReport>>>(x => null);
var uow = new Mock<IUnitOfWork>();
uow.SetupGet(x => x.ExecutionReports).Returns(mockExecRepo.Object);
var controller = new TradesController(uow.Object);
var query = new Query()
{
Field = field,
Operator = op,
Search = DateTime.Now.Add(TimeSpan.FromDays(dayManip)).Date.ToString("yyyy-MM-dd"),
SortDirection = sortDir
};
TradesController.TradesBody tb = new TradesController.TradesBody()
{
queries = new[] { query },
filterTerm = filterTerm
};
var results = controller.Get(tb, pageIndex, pageSize);
uow.Verify(mock => mock.ExecutionReports.GetFilteredTrades(new[] { query }, pageIndex, pageSize, filterTerm), Times.Once());
}
}
And the definitions of some of the objects I am mocking:
public interface IExecutionReportRepository : IRepository<ExecutionReport>
{
...
Task<IPagedResult<ExecutionReport>> GetFilteredTrades(IEnumerable<Query> queries, int pageIndex, int pageSize, string filterTerm);
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context, IExecutionReportRepository executionReportRepository)
{
_context = context;
ExecutionReports = executionReportRepository;
}
public IExecutionReportRepository ExecutionReports { get; }
}
TradesController:
public class TradesController : Controller
{
public class TradesBody
{
public IEnumerable<Query> queries;
public string filterTerm;
}
private readonly IUnitOfWork unitOfWork;
public TradesController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
/// <summary>
/// Gets a list of trades for the current date
/// </summary>
[HttpPost]
public async Task<IActionResult> Get([FromBody] TradesBody postBody, int pageIndex = 0, int pageSize = 100)
{
string filterTerm = postBody.filterTerm ?? "";
IEnumerable<Query> queries = postBody.queries;
IPagedResult<Domain.Entities.Core.ExecutionReport> queryResult;
queryResult = await unitOfWork.ExecutionReports.GetFilteredTrades(queries, pageIndex, pageSize, filterTerm); //Null reference error here
return Ok(queryResult);
}
}
When stepping through the code, I do not see any null objects and thus cannot actually see/understand where the null reference is actually being found, however I have noticed that I cannot see the method definition during debug time for 'GetFilteredTrades. Judging by this, my mocked method is not connecting to the method being executed, however I only have one GetFilteredTrades.
How do I resolve the null reference error being thrown in the TradesController and successfully run my test?
You are not setting up GetFilteredTrades to return anything so it is failing when you try to await it.
mockExecRepo
.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
.ReturnAsync(Mock.Of<IPagedResult<ExecutionReport>>()) //<--THIS
.Verifiable();
Also the method under test is async, so the test should be async as well.
[Test]
[TestCase(...)]
public async Task Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
and the method under test call awaited
var results = await controller.Get(tb, pageIndex, pageSize);
Finally, you are verifying the wrong mock based on the setup. Since the mockExecRepo setup has Verifiable() then you can simply call Verify() on the mock.
//...
//Act
var results = await controller.Get(tb, pageIndex, pageSize);
//Assert
mockExecRepo.Verify();
to verify that it was invoked as expected.
I am trying to flush my cache after I update an item, and I have tried a few different options and none are working as expected
public class PostApiController : Controller
{
private readonly IPostService _postService;
private readonly IPostTagService _postTagService;
private IMemoryCache _cache;
private MemoryCacheEntryOptions cacheEntryOptions;
public PostApiController(IPostService postService, IPostTagService postTagService, IMemoryCache cache)
{
_postService = postService;
_postTagService = postTagService;
_cache = cache;
cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromDays(1));
}
[HttpGet("{url}", Name = "GetPost")]
public IActionResult GetById(string url, bool includeExcerpt)
{
Post cacheEntry;
if (!_cache.TryGetValue($"GetById{url}{includeExcerpt}", out cacheEntry))
{
cacheEntry = _postService.GetByUrl(url, includeExcerpt);
_cache.Set($"GetById{url}{includeExcerpt}", cacheEntry, cacheEntryOptions);
}
if (cacheEntry == null)
{
return NotFound();
}
return new ObjectResult(cacheEntry);
}
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] Post item)
{
if (item == null)
{
return BadRequest();
}
var todo = _postService.GetById(id);
if (todo == null)
{
return NotFound();
}
_postService.Update(item);
_postTagService.Sync(item.Tags.Select(a => new PostTag { PostId = item.Id, TagId = a.Id }).ToList());
//Want to flush entire cache here
return new NoContentResult();
}
I have tried to Dispose() MemoryCache here but on next Api call, it is still disposed. Since the keys are somewhat dynamic, I can't just get the keys. How can I go about doing this?
You can store the dictionary instead. This way you can use the dynamic keys for entries and one single static key for the dictionary container, which can be stored in the cache instead of storing each entry separately.
Something like that:
private const string CachedEntriesKey = "SOME-STATIC-KEY";
[HttpGet("{url}", Name = "GetPost")]
public IActionResult GetById(string url, bool includeExcerpt)
{
Dictionary<string, Post> cacheEntries;
if (!_cache.TryGetValue(CachedEntriesKey, out cacheEntries))
{
cacheEntries = new Dictionary<string, Post>();
_cache.Set(CachedEntriesKey, cacheEntries);
}
var entryKey = $"GetById{url}{includeExcerpt}";
if (!cacheEntries.ContainsKey(entryKey))
{
return NotFound(); // by the way, why you do that instead of adding to the cache?
}
return new ObjectResult(cacheEntries[entryKey]);
}
Both TempData[] and static fields can be used to pass data from ActionResult to another.
Is there performance/memory difference? best practice?
I also noticed that when paging or sorting, the Detail function is recalled which lead that the list of cars should remain in memory.
public class TestController
{
private static IEnumerable<Cars> _cars;
public ActionResult Detials()
{
var uploadedCars = TempData["cars"] as IEnumerable<Cars>;
var testViewModel = new TestViewModel();
var result = TestViewModel.Process(uploadedCars);
//var result = TestViewModel.Process(_cars);
return View(result);
}
public ActionResult UploadCars(object obj)
{
// upload file ...
_cars= null; // reset value
//_cars= loader.GetAllCars(uploader);
TempData["cars"] = loader.GetAllCars(uploader);
return RedirectToAction("Detials");
}
}
}
I don't understand how to fix this issue. I will need your help and explanation on how to fix it.
I used a WebService to populate a DropDownList with artist names.
Here is the code behing.
private List<ArtistServiceReference.ArtistServiceClient> ArtistDetail()
{
ArtistServiceReference.ArtistServiceClient client =
new ArtistServiceReference.ArtistServiceClient();
ArtistServiceReference.Artist[] artists = client.ArtistDetail();
return artists.ToList(); <=== errror here
Cannot implicitly convert type System.Collections.Generic.List<ArtistServiceReference.Artist> to System.Collections.Generic.List<ArtistServiceReference.ArtistServiceClient>
Here is the ArtistService.cs
public class ArtistService : IArtistService
{
public List<Artist> ArtistDetail()
{
using (ArtistDataContext db = new ArtistDataContext())
{
return (from artist in db.Artists
select new Artist()
{
Id = artist.Id,
Artist_nom = artist.Artist_nom
}).ToList();
}
}
}
If you want your client method to return a list of Artists, why do you declare it as returning a list of ArtistClients?
The following should fix your issue:
private List<ArtistServiceReference.Artist> ArtistDetail()
{
...
return artists.ToList();
}
or, even more elegant:
using YourNamespace.ArtistServiceReference;
private List<Artist> ArtistDetail()
{
...
return artists.ToList();
}
Your return type on your method should be a list of ArtistServiceReference.Artist as opposed to a list of ArtistServiceReference.ArtistServiceClient. You want to return a list of Artists by using the ArtistServiceClient, you are not returning a list of the clients.
private List<ArtistServiceReference.Artist> ArtistDetail()
{
ArtistServiceReference.ArtistServiceClient client =
new ArtistServiceReference.ArtistServiceClient();
var artists = client.ArtistDetail();
return artists.ToList();
}
here is the solution:
code behind.cs
private List<ArtistServiceReference.Artist> ArtistDetail()
{
ArtistServiceReference.ArtistServiceClient client = new
ArtistServiceReference.ArtistServiceClient();
ArtistServiceReference.Voiture[] artists = client.ArtistDetail();
return artists.ToList();
}
ArtistService.cs
public class ArtistService : IArtistService
{
public List<Artist> ArtistDetail()
{
using (ArtistDataContext db = new ArtistDataContext())
{
return db.Artists.ToList();
}
}
}