Using DTO in Azure Mobile Service .NET throws target invocation exception - c#

I use Azure Mobile Services .NET backend and we all know we have to use Entity Framework classes to map to the database creation/migration.
So I need to use DTOs to serialize only the properties I want, computed properties etc. I'm following the Field Engineer example. But Automapper gave me so much pain although I did everything as supposed to.
I have checked couple of others blog and site, some use Automapper, others not, for example this one. I feel more comfortable not using Automapper and create DTOs on the fly with Select() as I was doing it before when implementing Web API.
I reverted TableController class to Post, use the EntityDomainManager and I left the GetAllPosts method to the following.
public class PostController : TableController<Post>
{
private MobileServiceContext _context;
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
_context = new MobileServiceContext();
DomainManager = new EntityDomainManager<Post>(_context, Request, Services);
}
//[ExpandProperty("User")]
// GET tables/Post
public IQueryable<PostDto> GetAllPost()
{
return Query().Include("User").Select(x => new PostDto());
}
}
I get the following error.
{"message":"An error has occurred.","exceptionMessage":"Exception has been thrown by the target of an invocation.","exceptionType":"System.Reflection.TargetInvocationException","stackTrace":" at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)\r\n at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)\r\n at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\r\n at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)\r\n at System.Web.Http.OData.Query.ODataQueryOptions.LimitResults(IQueryable queryable, Int32 limit, Boolean& resultsLimited)\r\n at System.Web.Http.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)\r\n at System.Web.Http.OData.EnableQueryAttribute.ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)\r\n at System.Web.Http.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n at System.Web.Http.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()","innerException":{"message":"An error has occurred.","exceptionMessage":"The specified type member 'DatePosted' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.","exceptionType":"System.NotSupportedException","stackTrace":" at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateSet(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ThenByTranslatorBase.Translate(ExpressionConverter parent, MethodCallExpression call)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateSet(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ThenByTranslatorBase.Translate(ExpressionConverter parent, MethodCallExpression call)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateSet(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()\r\n at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()\r\n at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()\r\n at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()\r\n at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()\r\n at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)\r\n at System.Web.Http.OData.Query.TruncatedCollection`1..ctor(IQueryable`1 source, Int32 pageSize)\r\n at System.Web.Http.OData.Query.ODataQueryOptions.LimitResults[T](IQueryable`1 queryable, Int32 limit, Boolean& resultsLimited)"}}
If I just convert it to the Entity Framework class it works. You will notice that I don't fill any property, just for testing purposes.
I test locally with IIS Express.
Data objects and models.
public class Post : EntityData
{
public DateTimeOffset DatePosted { get; set; }
public string StatusText { get; set; }
public PostType TypeOfPost { get; set; }
[ForeignKey("Id")]
public virtual User User { get; set; }
[ForeignKey("Id")]
public virtual ICollection<PostPhotoUrl> PhotoUrls { get; set; }
}
public class PostDto
{
public PostDto()
{
PhotoUrls = new HashSet<PostPhotoUrlDto>();
}
public DateTimeOffset DatePosted { get; set; }
public string StatusText { get; set; }
public int TypeOfPost { get; set; }
public UserDto User { get; set; }
public ICollection<PostPhotoUrlDto> PhotoUrls { get; set; }
}
Searching the internet couldn't find any other more clear tutorial how to use Azure Mobile Services and DTOs, though it shouldn't introduce such difficulty. If you have any resources are welcome.
I should mention that if I don't do the following the test website raises error when trying to test the endpoints.
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
The exception snapshot:
I don't REALLY need to have DTOs for this project, but if I don't resolve what I don't understand right now it will come back and hunt me in the long run of this service.

I'm not sure but think the problem may be with this line of code
return Query().Include("User").Select(x => new PostDto());
if youre NOT using AutoMapper then you will need to manually parse the properties in your Select statement something like this
return Query().Include("User")
.Select(x => new PostDto()
{
DatePosted = x.DatePosted,
StatusText = x.StatusText,
TypeOfPost = x.TypeOfPost,
User = new UserDto
{
//Your propertoes here
//eg Id = x.User.Id etc
}
);

It seems that the Select() LINQ expression conflicts before the elements are fully loaded or the references are still unset.
I converted the method to a IEnumerable return type and eager loaded the data before transforming to DTOs.
public IEnumerable<PostDto> GetAllPost()
{
return Query().Include("User").ToList().Select(x => new PostDto
{
DatePosted = x.DatePosted,
StatusText = x.StatusText,
TypeOfPost = (int)x.TypeOfPost,
User = new UserDto
{
Id = x.User.Id
}
});
}
After testing my service, it returned the following data I have inserted in my database.
[
{
"datePosted": "2015-04-27T04:05:38.843Z",
"statusText": "Post Text",
"typeOfPost": 1,
"user": {
"id": "a59d0f12-8bb1-448e-9c08-56f862b77ee4"
},
"photoUrls": []
}
]
Should test with AutoMapper now to cover that it works with it too.
Only unknown point is why I need to set the ReferenceLoopHandling property to Ignore for the service to show properly JSON sample data.
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

Related

Passing collection in querystring does not work with ODataQueryOptions

I have following existing GET endpoint
[HttpGet]
public async Task<IHttpActionResult> Get(ODataQueryOptions<Section> oDataOptions) {
//code here
return response;
}
I need to add a collection in querystring like <basequery>?$filter=id eq 1&includeCategory=A&includeCategory=B
so this would be bound as a collection in modified get endpoint as follows -
[HttpGet]
public async Task<IHttpActionResult> Get([FromUri (Name = "includeCategory"] ) List<Category> categories, ODataQueryOptions<Section> oDataOptions) {
//code here
return response;
}
Public enum Category {
A,
B
}
If I make postman request to this, I am getting An item with the same key has already been added.
System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Web.OData.Query.ODataQueryOptions..ctor(ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.Query.ODataQueryOptions`1..ctor(ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.ODataQueryParameterBindingAttribute.ODataQueryParameterBinding.CreateODataQueryOptions[T](ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.ODataQueryParameterBindingAttribute.ODataQueryParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
If my querystring has this <basequery>?$filter=id eq 1&includeCategory=A, this is working fine with ODataQueryOptions.
If my querystring has this <basequery>?$filter=id eq 1&includeCategory[0]=A&includeCategory[0]=B, this is working fine as well with ODataQueryOptions .
Same works for following with url <basequery>/1?includeCategory=A&includeCategory=B and endpoint with no $filter
[Route("{id}")
[HttpGet]
public async Task<IHttpActionResult> Get([FromUri (Name = "includeCategory"] ) List<Category> categories, long id) {
//code here
return response;
}
There is nothing in Section class that might collide with name Category.
That's why I am suspecting it's something to do with $filer, please suggest.

Validating Model Properties but allow null values

I am trying to get a model property validation to work but allowing a null value in a string property.
The Property i'm trying to validate is a:
public string PhoneNumber { get; set;}
And i am validating it like this:
[Phone(ErrorMessage = "Invalid telephone number.")]
public string PhoneNumber { get; set;}
The validation works great in my case except for when a value is not sent in for Phone number, to the api with an object.
is there any thing like a: [AllowNullValue] attribute or how do i get null values pass the "Phone" attribute?
UPDATE (Anton Gorbunov's post):
{
"Message": "An error has occurred.",
"ExceptionMessage": "The field is not a valid phone number.",
"ExceptionType": "System.ComponentModel.DataAnnotations.ValidationException",
"StackTrace": " at System.ComponentModel.DataAnnotations.ValidationAttribute.Validate(Object value, String name)\r\n at RABE_BCV_API.Controllers.APIController.UpsertMember(MemberModel memberObject) in C:\\Users\\John\\Documents\\Visual Studio 2015\\Projects\\RABE_BCV_API\\RABE_BCV_API\\Controllers\\APIController.cs:line 29\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
It looks strange, because is the first instruction of IsValid Metod in PhoneAttribute is:
public override bool IsValid(object value) {
if (value == null) {
return true;
}
...
}
Maybe you use [Required] or [BindRequired] attributes?

Registrations not resolving

The auto-resolving of the .Net Core framework is not finding my registrations inside DryIoC.
This using the new .Net Core 2 framework, DryIoC 2.10.7 and DryIoc.Microsoft.DependencyInjection 1.2.2. I cannot update to DryIoC 2.11.7 because the DryIoc.Microsoft.DependencyInjection does not pick up the latest version. I did attempt to re-install the latter after the 2.11.7 install but that did not work (ambiguous references).
Here is my startup code:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddControllersAsServices();
var container = new Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
container.ConfigureServiceProvider<CompositionRoot>();
}
And here are my registration code:
public CompositionRoot(IRegistrator registrator, IContainer container)
{
//System Clock
registrator.Register<IClock, ConcreteClock>(Reuse.Singleton, null, null, IfAlreadyRegistered.Keep);
container.RegisterInstance<ISystemConfiguration>(new Configuration(container.Resolve<IClock>()), Reuse.Singleton, IfAlreadyRegistered.Keep);
//Logging
container.RegisterInstance<ILogging>(CreateLogger(container.Resolve<ISystemConfiguration>()), Reuse.Singleton, IfAlreadyRegistered.Keep);
}
Finally the controller:
public class AdminController : Controller
{
private readonly ILogging _log;
private readonly IClock _clock;
public AdminController(ILogging log,
IClock clock)
{
_log = log;
_clock = clock;
}
}
Here is the exception:
System.InvalidOperationException: Unable to resolve service for type
'SharedAssets.Interfaces.ILogging' while attempting to activate
'DataDictionaryService.Controllers.AdminController'. at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, ISet1 callSiteChain,
ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type
serviceType, Type implementationType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor
descriptor, Type serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type
serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type
serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type
serviceType, ServiceProvider serviceProvider) at
System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary2
dictionary, TKey key, Func`3 valueFactory, TArg arg) at
Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type
serviceType) at
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider
provider, Type serviceType) at
Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator.Create(ControllerContext
actionContext) at
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController0(ControllerContext
controllerContext) at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext
context) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next,
Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
Thanks!
When using custom DI frameworks you need to update the ConfigureServices to return your custom IServiceProvider.
public IServiceProvider ConfigureServices(IServiceCollection services) {
services.AddMvc()
.AddControllersAsServices();
var container = new Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
var provider = container.ConfigureServiceProvider<CompositionRoot>();
return provider
}
At runtime, your provider will be used to resolve types and inject dependencies.
Reference:
Introduction to Dependency Injection in ASP.NET Core: Replacing the default services container

Using ActionFilterAttribute for model validation

I am trying to use ActionFilterAttribute to validate the model when a call is made to my Web API. I have added the following
public class ValidateModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
I have added standard validators to my model properties and it all works fine. However when I add a custom class level validator it fails with the following error.
A validation attribute of type XXXValidatorAttribute cannot be used to
validate values."
StackTrace: at
Microsoft.Practices.EnterpriseLibrary.Validation.Validators.BaseValidationAttribute.IsValid(Object
value)\r\n at
System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object
value, ValidationContext validationContext)\r\n at
System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object
value, ValidationContext validationContext)\r\n at
System.Web.Http.Validation.Validators.DataAnnotationsModelValidator.Validate(ModelMetadata
metadata, Object container)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.ShallowValidate(ModelMetadata
metadata, ValidationContext validationContext, Object container,
IEnumerable'1 validators)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata
metadata, ValidationContext validationContext, Object container,
IEnumerable'1 validators)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object
model, Type type, ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, String keyPrefix)\r\n at
System.Web.Http.ModelBinding.FormatterParameterBinding.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
System.Web.Http.Controllers.HttpActionBinding.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter'1.GetResult()\r\n at
System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()
This validator works fine when I invoke it via a unit test using a validation factory. I am not sure why it fails during ModelState validation? What is the difference between the two?
I have the model defined as
I have a model defined as
[CashDetailValidator()]
public sealed class CashDetails : DetailsBase
{
// Reference Number
[ValidatorComposition(CompositionType.Or)]
[NotNullValidator(Negated=true)]
[StringLengthValidator(1, RangeBoundaryType.Inclusive, 16, RangeBoundaryType.Exclusive, MessageTemplate = "Reference number should be between 1 to 16 characters in length.")]
public string RelatedReference { get; set; } ...........................
}}
The composition validator also gives the same error.

CreateDocumentQuery throws exception when used with LINQ, fine with SQL

I am attempting to retrieve a single entity stored in my Azure DocumentDb. I have discovered that my code only works when I supply the query by SQL, like the below:
var query = String.Format("SELECT * FROM UserDetail u WHERE u.id = '{0}'", id);
return _client.CreateDocumentQuery<TEntity>(_selfLink, query).AsEnumerable().FirstOrDefault();
However using a LINQ expression, as in the following two examples, it fails with an exception:
// EXCEPTION
return _client.CreateDocumentQuery<TEntity>(_selfLink).Where(u => u.Id.ToString() == id).AsEnumerable().FirstOrDefault();
// EXCEPTION
return (from u in _client.CreateDocumentQuery<TEntity>(_selfLink)
where u.Id.ToString() == id
select u).AsEnumerable().FirstOrDefault();
The rather monstrous stack trace of the exception raised is:
System.AggregateException: One or more errors occurred. ---> Microsoft.Azure.Documents.Linq.DocumentQueryException: Unhandled expression type: 'Call'
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarExpression(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitBinary(BinaryExpression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarExpression(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarLambda(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitWhere(ReadOnlyCollection`1 arguments, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitMethodCall(MethodCallExpression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.Translate(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.TranslateQuery(Expression inputExpression)
at Microsoft.Azure.Documents.Linq.SQLTranslator.TranslateQuery(Expression inputExpression)
at Microsoft.Azure.Documents.Linq.DocumentQueryEvaluator.HandleMethodCallExpression(MethodCallExpression expression, QueryType defaultQueryType, QueryType& queryType)
at Microsoft.Azure.Documents.Linq.DocumentQueryEvaluator.Evaluate(Expression expression, QueryType defaultQueryType, QueryType& queryType)
at Microsoft.Azure.Documents.Linq.DocumentQueryExecutionContext.<ExecuteAllAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.Azure.Documents.Linq.DocumentQuery`1.<GetEnumeratorTAsync>d__10.MoveNext()
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at Microsoft.Azure.Documents.Linq.DocumentQuery`1.GetEnumerator()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at Cherish.Domain.Repositories.Implementation.DocumentRepository`1.FindById(String id) in \\psf\home\Documents\Visual Studio 2013\Projects\Cherish\Cherish.Domain\Repositories\Implementation\DocumentRepository.cs:line 82
---> (Inner Exception #0) Microsoft.Azure.Documents.Linq.DocumentQueryException: Unhandled expression type: 'Call'
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarExpression(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitBinary(BinaryExpression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarExpression(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitScalarLambda(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitWhere(ReadOnlyCollection`1 arguments, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.VisitMethodCall(MethodCallExpression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.Translate(Expression inputExpression, TranslationContext context)
at Microsoft.Azure.Documents.Linq.ExpressionToSql.TranslateQuery(Expression inputExpression)
at Microsoft.Azure.Documents.Linq.SQLTranslator.TranslateQuery(Expression inputExpression)
at Microsoft.Azure.Documents.Linq.DocumentQueryEvaluator.HandleMethodCallExpression(MethodCallExpression expression, QueryType defaultQueryType, QueryType& queryType)
at Microsoft.Azure.Documents.Linq.DocumentQueryEvaluator.Evaluate(Expression expression, QueryType defaultQueryType, QueryType& queryType)
at Microsoft.Azure.Documents.Linq.DocumentQueryExecutionContext.<ExecuteAllAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.Azure.Documents.Linq.DocumentQuery`1.<GetEnumeratorTAsync>d__10.MoveNext()<---
For reference, this is the UserDetail class (irrelevant properties and methods removed):
public class UserDetail : IIdentifiableEntity
{
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
[JsonProperty(PropertyName = "fn")]
public string Firstname { get; set; }
[JsonProperty(PropertyName = "ln")]
public string Lastname { get; set; }
[JsonProperty(PropertyName = "contribs")]
public IList<ContributorRelation> Contributors { get; set; }
}
I know that I can use the SQL-format method, however as a personal preference I would like to use Lambdas, and am at a loss to understand why my code does not work as it should. Do I need to do some other type of type coersion from Guid?
The call to the ToString method is not supported inside linq queries. Try parsing the id to guid and do a simple equality like this:
return _client.CreateDocumentQuery<TEntity>(_selfLink).Where(u => u.Id == Guid.Parse(id)).AsEnumerable().FirstOrDefault();

Categories

Resources