IQueryable returning nulls, IEnumerable does not - c#

I have an extension method that needs to return some DTOs based on an IQueryable it receives. Tinkering with the code today (I added a new int property) I realized the method was not working properly anymore, returning part of the DTOs list as nulls.
I wanted to see if maybe a property I added was the problem so I trimmed the method to how it looks below and it stills returns some nulls.
public static IQueryable<OperationDto> SelectDto(this IQueryable<Operation> operations)
{
return operations.Select(op=> new OperationDto
{
Id = op.Id
});
}
I changed the method to
public static IEnumerable<OperationDto> SelectDto(this IQueryable<Operation> operations)
{
return operations.AsEnumerable().Select(op=> new OperationDto
{
Id = op.Id
});
}
and it works again without changing anything else.
I don't understand why the IQueryable<OperationDto> suddenly stopped working (maybe the new property) and why the IEnumerable variant works.
EDIT
The extension method is called in the following repository method:
original IQueryable version:
public IQueryable<OperationDto> FindOperationDtos(OperationModelFilter modelFilter)
{
IQueryable<Operation> filteredData = DbSet.AsQueryable();
if (modelFilter.UserId.HasValue)
filteredData = filteredData.Where(o => o.User.Id == modelFilter.UserId.Value);
...
return filteredData.Distinct().SelectDto();
}
IEnumerable version:
public IQueryable<OperationDto> FindOperationDtos(OperationModelFilter modelFilter)
{
IQueryable<Operation> filteredData = DbSet.AsQueryable();
if (modelFilter.UserId.HasValue)
filteredData = filteredData.Where(o => o.User.Id == modelFilter.UserId.Value);
...
return filteredData.Distinct().SelectDto();
}

Related

Detect or pass calling method in Entity Framework 6 and Entity Framework Core via IDbCommandInterceptor

I'm interested in capturing the name of the method that initiated an Entity Framework query. I will be using this functionality in both Entity Framework 6 and Entity Framework Core
Consider the following class:
namespace MyCompany.MyApp
{
public class MyDataClass
{
// ... snip ...
public Person GetPerson(long id)
{
return _dbContext
.Person
.FirstOrDefault(p => p.Id == id);
}
}
}
And the following interceptor:
public class EFCommandInterceptor : IDbCommandInterceptor
{
// ... snip other interface methods ...
public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
{
var callingMehtod = GetCallingMethod() // <--- I'd like this to return the string "MyCompany.MyApp.MyDataClass.GetPerson"
}
}
Is there any way to determine what method initiated the query that resulted in the interceptor being called?
As I'm going to be using this functionality for tracing and performance analysis, I can't send and record the entire Environment.StackTrace for every query running through the system.
I also considered using the StackTrace class in conjunction with stackTrace.GetFrames() to try to analyze the call stack to find the actual method that initiated the query, but it's not clear how to do this reliably without relying on some kind of namespace convention that universally identifies a data access class.
Another approach that would be acceptable might look something like:
namespace MyCompany.MyApp
{
public class MyDataClass
{
// ... snip ...
public Person GetPerson(long id)
{
return _dbContext
.Person
.WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
.FirstOrDefault(p => p.Id == id);
}
}
}
The intention with the above example would be able to later retrieve the extra context via the DbCommandInterceptionContext inside IDbCommandInterceptor
Ultimately, the problem I'm trying to solve is to get the fully qualified name of the method that initiated a query without having a full call stack.
With EF Core you can use TagWith
var result = db.Albums
.TagWith(MethodBase.GetCurrentMethod().Name)
.ToList();
You could simply register the information before running the query, something like:
public Person GetPerson(long id)
{
EfDebug.SetCurrentMethodName();
return _dbContext
.Person
.WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
.FirstOrDefault(p => p.Id == id);
}
. . .
static class EfDebugExtensions
{
public static AsyncLocal<string> CurrentMethodName = new AsyncLocal<string>();
public static void SetCurrentMethodName([CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
}
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source,[CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
var rv = Enumerable.FirstOrDefault(source);
return rv;
}
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source, [CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
var rv = Enumerable.ToList(source);
return rv;
}
}

Setup method with FindOptions<T> as parameter in UnitTest using Moq

Imagine I have this class:
public class MyClass
{
public string Id { get; set; }
public string Name { get; set; }
public IList<MyOtherClass> MyOtherClassList { get; set; }
}
And this method on my service layer which I want to unit-test with Moq and xunit:
public IList<MyClass> GetAll()
{
var options = new FindOptions<MyClass>
{
Projection = Builders<MyClass>.Projection
.Exclude(m => m.MyOtherClassList)
};
return MyClassRepository.GetAll(options)
}
And this would be the test:
[Fact]
public void GetAll_Success()
{
//Arrange
List<MyClass> expected = ... ;
var mockMyClassRepository = new Mock<IMyClassRepository>();
mockMyClassRepository.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>())).Returns(expected);
var myClassService = new MyClassService(mockMyClassRepository);
//Act
var result = myClassService.GetAll();
//Assert
Assert.Equal(expected, result);
mockMyClassRepository.Verify(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()), Times.Once);
}
I want to avoid using It.IsAny for the FindOptions and be sure that the findOptions are excluding the MyOtherClassList attribute but I'm not sure how to compare the FindOptions class.
Any suggestion?
Hows this
mockMyClassRepository
.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns(FindOptions<MyClass> a) =>
{
if (a.MyOtherClass != null || a.MyOtherClass.Length() > 0)
throw new InvalidOperationException();
else
return expected;
});
You can use an expression in the Returns to inspect the provided argument so you can apply what ever filtering you want.
mockMyClassRepository
.Setup(_ => _.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns((FindOptions<MyClass> options) => {
var projection = options.Projection;
//...use as desired to filter the expected.
return expected;
});
The same could have been done in a Callback where the expected could be manipulated/altered, again based on the passed argument.
Do note that what ever is done with the passed argument is an implementation concern that applies to what is done by the actual repository, which you are trying to mock.
The only way to be sure that the findOptions are actually excluding the MyOtherClassList attribute would be via an integration test.
If you're looking to be more specific on the .Verify call, you can leave the .Setup call to use It.IsAny, then use additional constrains on the verify call, like this:
mockMyClassRepository.Verify(m => m.GetAll(It.Is<FindOptions<MyClass>>(options =>
options.Projection == .... //whatever logic would return true for your success criteria
)), Times.Once);

MVC application throws ObjectDisposedException eventhough I used ToList() method

I am writing MVC web application and I've got stucked at one point.
The application throws ObjectDisposedException eventhough I used ToList() method while dbContext was still open. Here is my code:
Data Access Layer:
public List<CompanyCode> SearchCompanyCodes(int? projectId=null)
{
IQueryable<sl_CompanyCodes> filtered=dbContext.sl_CompanyCodes;
if(projectId!=null)
{
filtered = filtered.Where(cc => cc.Project_ID == projectId);
}
return filtered.ToList();
}
Bussiness logic layer:
public List<CompanyCode> CompanyCodes(int? cc_projectId=null)
{
List<CompanyCode> result;
using(var repository=new E6Repository())
{
result=repository.SearchCompanyCodes(projectId:cc_projectId);
}
// E6Repository.Dispose calls dbContext.Dispose()
return result;
}
Controller:
public JsonResult CompanyCodes()
{
var logic = new GetDataLogic();
List<CompanyCode> l = logic.CompanyCodes();
return Json(l, JsonRequestBehavior.AllowGet);
}
I have no idea what to do... When I stop debugger just before return action in controller, I can see well filled list when I type l in immediate window (notice at this point dbContext is already disposed).
Interesting thing: if I change ActionResult into view with IEnumerable<CompanyCode> as model, response is fine and there is no errors.
Can anyone explain me what's going on?
I think you have not created an instance of your EF context. Try this:
public List<CompanyCode> SearchCompanyCodes(int? projectId=null)
{
using (var context = new dbContext())
{
IQueryable<sl_CompanyCodes> filtered = from compCodes in dbContext.sl_CompanyCodes
select compCodes;
}
if(projectId!=null)
{
filtered = filtered.Where(cc => cc.Project_ID == projectId);
}
return filtered.ToList();
}

Azure DocumentDb error "Query must evaluate to IEnumerable"

I am having issues in trying to query my Azure DocumentDb storage account when attempting to retrieve a single record. This is my WebAPI code:
// Controller...
public AccountController : ApiController {
// other actions...
[HttpGet]
[Route("Profile")]
public HttpResponseMessage Profile()
{
var userId = User.Identity.GetUserId();
var rep = new DocumentRepository<UserDetail>();
var profile = rep.FindById(userId);
if (profile == null)
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Profile not found");
return Request.CreateResponse(HttpStatusCode.OK, profile);
}
}
// Repository
public class DocumentRepository<TEntity> : IDocumentRepository<TEntity> where TEntity : IIdentifiableEntity
{
private static DocumentClient _client;
private static string _databaseName;
private static string _documentsLink;
private static string _selfLink;
public DocumentRepository()
{
_client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["DocumentDbEndpointUrl"]), ConfigurationManager.AppSettings["DocumentDbAuthKey"]);
_databaseName = ConfigurationManager.AppSettings["DocumentDbDatabaseName"];
var _database = ReadOrCreateDatabase();
var collection = InitialiseCollection(_database.SelfLink, EntityName);
_documentsLink = collection.DocumentsLink;
_selfLink = collection.SelfLink;
}
// other methods...
public TEntity FindById(string id)
{
return _client.CreateDocumentQuery<TEntity>(_documentsLink).SingleOrDefault(u => u.Id.ToString() == id);
}
}
It is this FindById method which causes the following issue:
An exception of type 'Microsoft.Azure.Documents.Linq.DocumentQueryException' occurred in Microsoft.Azure.Documents.Client.dll but was not handled in user code
Additional information: Query expression is invalid, expression return type
Foo.Models.DocumentDbEntities.UserDetail is unsupported. Query must evaluate to IEnumerable.
I don't understand what this error means, or how I fix it. I don't wish to return an IEnumerable or any descendant class as this method will return either 0 or 1 records. It works if I remove the SingleOrDefault clause, and change the return type to an IQueryable, however this is not what I want.
SingleOrDefault() is not supported, yet in the LINQ provider.
Change this to .Where(u => u.Id.ToString() == id).AsEnumberable().FirstOrDefault();
I can't say why Ryan's syntax stopped working for you, but you should be able to work around it without the extra performance hit by using a CreateDocumentQuery<>() overload with an explicitly-defined query string instead of using .Where():
string query = string.Format("SELECT * FROM docs WHERE docs.id = \"{0}\"", id);
return _client.CreateDocumentQuery<TEntity>(DocumentsLink, query).AsEnumerable().FirstOrDefault();
You might need to play with the query a little, but something of that form ought to work.

How to unit test an Action method which returns JsonResult?

If I have a controller like this:
[HttpPost]
public JsonResult FindStuff(string query)
{
var results = _repo.GetStuff(query);
var jsonResult = results.Select(x => new
{
id = x.Id,
name = x.Foo,
type = x.Bar
}).ToList();
return Json(jsonResult);
}
Basically, I grab stuff from my repository, then project it into a List<T> of anonymous types.
How can I unit-test it?
System.Web.Mvc.JsonResult has a property called Data, but it's of type object, as we expected.
So does that mean if I want to test that the JSON object has the properties I expect ("id", "name", "type"), I have to use reflection?
EDIT:
Here's my test:
// Arrange.
const string autoCompleteQuery = "soho";
// Act.
var actionResult = _controller.FindLocations(autoCompleteQuery);
// Assert.
Assert.IsNotNull(actionResult, "No ActionResult returned from action method.");
dynamic jsonCollection = actionResult.Data;
foreach (dynamic json in jsonCollection)
{
Assert.IsNotNull(json.id,
"JSON record does not contain \"id\" required property.");
Assert.IsNotNull(json.name,
"JSON record does not contain \"name\" required property.");
Assert.IsNotNull(json.type,
"JSON record does not contain \"type\" required property.");
}
But I get a runtime error in the loop, stating "object does not contain a definition for id".
When I breakpoint, actionResult.Data is defined as a List<T> of anonymous types, so I figure if I enumerate through these, I can check the properties. Inside the loop, the object does have a property called "id" - so not sure what the issue is.
I know I'm a bit late on this guys, but I found out why the dynamic solution wasn't working:
JsonResult returns an anonymous object and these are, by default, internal, so they need to be made visible to the tests project.
Open your ASP.NET MVC application project and find AssemblyInfo.cs from folder called Properties. Open AssemblyInfo.cs and add the following line to the end of this file.
[assembly: InternalsVisibleTo("MyProject.Tests")]
Quoted from: http://weblogs.asp.net/gunnarpeipman/archive/2010/07/24/asp-net-mvc-using-dynamic-type-to-test-controller-actions-returning-jsonresult.aspx
I thought it would be nice to have this one for the record. Works like a charm
RPM, you look to be correct. I still have much to learn about dynamic and I cannot get Marc's approach to work either. So here is how I was doing it before. You may find it helpful. I just wrote a simple extension method:
public static object GetReflectedProperty(this object obj, string propertyName)
{
obj.ThrowIfNull("obj");
propertyName.ThrowIfNull("propertyName");
PropertyInfo property = obj.GetType().GetProperty(propertyName);
if (property == null)
{
return null;
}
return property.GetValue(obj, null);
}
Then I just use that to do assertions on my Json data:
JsonResult result = controller.MyAction(...);
...
Assert.That(result.Data, Is.Not.Null, "There should be some data for the JsonResult");
Assert.That(result.Data.GetReflectedProperty("page"), Is.EqualTo(page));
I'm a bit late to the party, but I created a little wrapper that lets me then use dynamic properties. As of this answer I've got this working on ASP.NET Core 1.0 RC2, but I believe if you replace resultObject.Value with resultObject.Data it should work for non-core versions.
public class JsonResultDynamicWrapper : DynamicObject
{
private readonly object _resultObject;
public JsonResultDynamicWrapper([NotNull] JsonResult resultObject)
{
if (resultObject == null) throw new ArgumentNullException(nameof(resultObject));
_resultObject = resultObject.Value;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (string.IsNullOrEmpty(binder.Name))
{
result = null;
return false;
}
PropertyInfo property = _resultObject.GetType().GetProperty(binder.Name);
if (property == null)
{
result = null;
return false;
}
result = property.GetValue(_resultObject, null);
return true;
}
}
Usage, assuming the following controller:
public class FooController : Controller
{
public IActionResult Get()
{
return Json(new {Bar = "Bar", Baz = "Baz"});
}
}
The test (xUnit):
// Arrange
var controller = new FoosController();
// Act
var result = await controller.Get();
// Assert
var resultObject = Assert.IsType<JsonResult>(result);
dynamic resultData = new JsonResultDynamicWrapper(resultObject);
Assert.Equal("Bar", resultData.Bar);
Assert.Equal("Baz", resultData.Baz);
Here's one I use, perhaps it is of use to anyone. It tests an action that returns a JSON object for use in clientside functionality. It uses Moq and FluentAssertions.
[TestMethod]
public void GetActivationcode_Should_Return_JSON_With_Filled_Model()
{
// Arrange...
ActivatiecodeController activatiecodeController = this.ActivatiecodeControllerFactory();
CodeModel model = new CodeModel { Activation = "XYZZY", Lifespan = 10000 };
this.deviceActivatieModelBuilder.Setup(x => x.GenereerNieuweActivatiecode()).Returns(model);
// Act...
var result = activatiecodeController.GetActivationcode() as JsonResult;
// Assert...
((CodeModel)result.Data).Activation.Should().Be("XYZZY");
((CodeModel)result.Data).Lifespan.Should().Be(10000);
}
I extend the solution from Matt Greer and come up with this small extension:
public static JsonResult IsJson(this ActionResult result)
{
Assert.IsInstanceOf<JsonResult>(result);
return (JsonResult) result;
}
public static JsonResult WithModel(this JsonResult result, object model)
{
var props = model.GetType().GetProperties();
foreach (var prop in props)
{
var mv = model.GetReflectedProperty(prop.Name);
var expected = result.Data.GetReflectedProperty(prop.Name);
Assert.AreEqual(expected, mv);
}
return result;
}
And i just run the unittest as this:
- Set the expected data result:
var expected = new
{
Success = false,
Message = "Name is required"
};
- Assert the result:
// Assert
result.IsJson().WithModel(expected);
My solution is to write the extension method:
using System.Reflection;
using System.Web.Mvc;
namespace Tests.Extensions
{
public static class JsonExtensions
{
public static object GetPropertyValue(this JsonResult json, string propertyName)
{
return json.Data.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public).GetValue(json.Data, null);
}
}
}
If in the test you know what exactly the Json data result should be then you can just do something like this:
result.Data.ToString().Should().Be(new { param = value}.ToString());
P.S. This would be if you had used FluentAssertions.Mvc5 - but it shouldn't be hard to convert it to whatever testing tools you use.
This is how I assert it
foreach (var item in jsonResult.Data as dynamic) {
((int)item.Id).ShouldBe( expected Id value );
((string)item.name).ShouldBe( "expected name value" );
}
You can convert the value property of the JsonResult into an instance of a known type and then simply access it's properties.
public static class JsonResultExtensions
{
public static T ExtractType<T>(this JsonResult result)
{
var resultAsJson = JsonSerializer.Serialize(result.Value);
return JsonSerializer.Deserialize<T>(resultAsJson);
}
}
below is an example of the use of the extension method:
MyModel model = jsonResult.ExtractType<MyModel>();
Assert.True(model.Success);

Categories