I have been searching a lot, both online, and debugging, re-read docs for dryioc and took a look at the samples. I don't think my issues lies with dryioc, or maybe I'm overlooking a silly mistake.
Project setup => service => owin + webapi2
Using attributerouting, swagger and apiversioning
This is how I setup my container
private IContainer SetupDependencyInjection(HttpConfiguration config)
{
var container = new Container();
container.WithWebApi(config, throwIfUnresolved: type => typeIsController());
container.RegisterWebApiControllers(config);
container.Register<IObjectFactory, ObjectFactory>(Reuse.Singleton);
RegisterStoreServices(container);
RegisterMetadata(container);
var validate = container.Validate();
var verify = container.VerifyResolutions();
if (validate.Any() || verify.Any())
{
throw new Exception("Error occured while setting up service dependencies.");
}
using (var c = container.OpenScope(Reuse.WebRequestScopeName))
{
var songController = c.Resolve<Musify.WebApi.Controllers.v1._0.SongsController>();
var GenresController = c.Resolve<Musify.WebApi.Controllers.v1._0.GenresController>();
}
return container;
}
Both RegisterStoreServices and RegisterMetadata only contain singleton or transient registrations.
When I try to call a controller method on my API controller, I get the following error
{
"message": "An error has occurred.",
"exceptionMessage": "An error occurred when trying to create a controller of type 'SongsController'. Make sure that the controller has a parameterless public constructor.",
"exceptionType": "System.InvalidOperationException",
"stackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
"innerException": {
"message": "An error has occurred.",
"exceptionMessage": "Unable to resolve Musify.Api.Service.Data.ISongRepository as parameter \"songRepository\"\r\n in scoped Musify.WebApi.Controllers.v1._0.SongsController.\r\nWhere no service registrations found\r\n and number of Rules.FallbackContainers: 0\r\n and number of Rules.UnknownServiceResolvers: 0",
"exceptionType": "DryIoc.ContainerException",
"stackTrace": " at DryIoc.Throw.It(Int32 error, Object arg0, Object arg1, Object arg2, Object arg3)\r\n at DryIoc.Container.ThrowUnableToResolve(Request request)\r\n at DryIoc.Container.DryIoc.IContainer.ResolveFactory(Request request)\r\n at DryIoc.ReflectionFactory.CreateExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetDelegateOrDefault(Request request)\r\n at DryIoc.Container.ResolveAndCacheDefaultDelegate(Type serviceType, Boolean ifUnresolvedReturnDefault, IScope scope)\r\n at DryIoc.Container.DryIoc.IResolver.Resolve(Type serviceType, Boolean ifUnresolvedReturnDefault)\r\n at DryIoc.WebApi.DryIocDependencyScope.GetService(Type serviceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}
I know that in a lot of cases, this is because the service is not registered, but I did.
container.Register<ISongRepository, SongRepository>(Reuse.Singleton);
As you can see in the fist code snippet, I valide, verify, and I even tried container.openscope, to see if the container was registered. And on the container, I can see the songrepository being filled in as well.
So I'm guessing, somehow, the webapi doesn't use the controllers inujected into the container.
Am I doing something wrong in my dependency setup?
public void Configuration(IAppBuilder appBuilder)
{
SetupLogging();
var config = new HttpConfiguration();
SetupRouting(config);
SetupVersioning(config);
SetupJson(config);
SetupDocumentation(config);
SetupDependencyInjection(config);
appBuilder.UseWebApi(config);
appBuilder.UseErrorPage(ErrorPageOptions.ShowAll);
}
I'm not sure if it matters, but I'm setting the container up, after everything else has been configured.
After some messing around, I got it working after adding the lines marked with //this
var container = SetupDependencyInjection(config);
appBuilder.UseDryIocOwinMiddleware(container); //this
appBuilder.UseWebApi(config);
appBuilder.UseDryIocWebApi(config); //this
I would like an explenation though, if someone could give me one.
Because on another post, I read that a guy had problems because of using both UseWebApi and UseDryIocWebApi.
But without the OwinMiddleware, it still wouldn't work. So what do both of these functions accomplish?
Or did I use a "wrong fix"?
Related
I am trying to port code that had been using Microsoft.WindowsAzure.Storage classes to use the newer classes in Azure.Data.Tables, Azure.Storage.Queues, etc. From what I have been able to discern, the StorageException class has been replaced by RequestFailedException. Unfortunately, there are some properties in StorageException that do not exist in RequestFailedException, making it difficult to log appropriate messages when an exception is encountered (for example: RequestId, RequestInformation, etc.).
The migration document does not address the differences between StorageException and the new RequestFailedException, or how to get error details from it.
It seems that either the new libraries are not yet mature enough for prime time, or maybe it is just because the documentation is lacking the relevant information and I can't find the appropriate methodologies for getting all of the error information from the RequestFailedException.
Does anyone know how to get more data out of the new class? Here are some examples of what we used to do:
catch (StorageException e)
{
operation.Telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
Changing the above to use RequestFailedException is a problem because RequestInformation is not a property of RequestFailedException.
Here is another case:
catch (StorageException se)
{
var ri = se.RequestInformation;
if (ri.ErrorCode == "TableNotFound")
{
Logger.Info(
$"{SJResult.MakeInfo(64)} {ri.HttpStatusCode} {ri.HttpStatusMessage}, Storage Service code={ri.ErrorCode} This is OK if HL7 has not yet received messages."); // 60240040
}
else
{
Logger.Error(
$"{SJResult.MakeError(65)} HttpStatusCode: {ri.HttpStatusCode}, HttpStatusMessage: {ri.HttpStatusMessage}, Storage Service code={ri.ErrorCode}, " +
$"Extended.ErrorCode: {ri.ExtendedErrorInformation.ErrorCode} Extended.ErrorMessage: {ri.ExtendedErrorInformation.ErrorMessage}"); // E0240041
throw;
}
Again, RequestInformation is not available in RequestFailedException.
How do we get access to all the detailed information (RequestInformation) about an exception from the new RequestFailedException class?
As you can see the definition of RequestFailedException Class (Azure) and constuctors in the latest version of azure sdk.
RequestFailedException(Int32, String, String, Exception) : gives HTTP status code ,specified error message, error code, and a reference to the inner exception .
And
RequestFailedException(Response)
Gives error message, HTTP status code, error code obtained from the specified response.
The response in the argument represents the HTTP response from the service which has ClientRequestId as one of the properties as shown in the table which gets client request id that was sent to server in the form of x-ms-client-request-id headers.You can try the same while catching the error in the try-catch block.
In exception class you can give
public class RequestFailedException : Exception
{
...
public RequestFailedException(int status, string message, string? errorCode, Exception? innerException) : base(message , innerException) { }
}
Or use RequestFailedException(Response) from which you can get ClientRequestId.
I’ve not tested it myself, but please check if below can be worked around which is taken from the below references or check if something similar can give an idea.Also see if content property can be retrieved as a part of response.
try
{
...
}
catch (Exception aex)
{
foreach (var ex in aex.InnerExceptions)
{
if (ex is RequestFailedException except)
{
var innerException = excep.InnerException;
if (innerException != null && innerException.GetType() == typeof(WebException))
{
WebException webEx = innerException as WebException;
WebResponse resp = webEx.Response;
var responseHeaders = resp.Headers;
string requestId = responseHeaders["x-ms-request-id"];
Console.WriteLine("Request Id: " + requestId);
Console.WriteLine(except.InnerException.Message);
}
else
{
// (not a RequestFailedException)
Console.WriteLine($"{ex.Message}");
}
}
References:
How can I get Request ID when an exception occurs? (microsoft.com)
c# - How can you catch a RequestFailedException if making multiple
DownloadToAsync calls in parallel? - Stack Overflow
I have a Web API ApiController base class and I would like to perform some validations in the constructor. This might include checking the current load on the server. If it's high, I'd like to return an appropriate HttpResponseMessage indicating the requestor should try again later.
Is something like this possible?
I Haven't tested it but that's not what the constructor is for. I don't think all plumbing is set at that time.
You could use global filters for this purpose. Here you have a sample that sets a global filter for authorization, you should use a similar logic but creating your own filter for your specific purposes.
A global filter would intercept all your requests and is executed before the controller actions so is a good place to perform your task.
Even though what you are doing sounds like it may be better to revise the approach. Note that you can throw HttpResponseException since the WebApi is Rest Service HttpResponseException is the recommended way to throw Exceptions back to the client.
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("No idea what happened "),
ReasonPhrase = "Something was not Not Found"
}
throw new HttpResponseException(resp);
As long as you're using .NET 4.5, then you'd be better off creating a custom MessageHandler. You'll need to extend DelegatingHandler in order to do that.
public class MyHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(
HttpMessageRequest request, CancellationToken cancellationToken) {
// Access the request object, and do your checking in here for things
// that might cause you to want to return a status before getting to your
// Action method.
// For example...
return request.CreateResponse(HttpStatusCode.Forbidden);
}
}
Then inside your WebApiConfig, just add the following code to use the new Handler:
config.MessageHandlers.Add(new MyHandler());
You can't throw HttpResponseException in constructor, that will always cause 500.
Easiest way is to override ExecuteAsync():
public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) {
if (!myAuthLogicCheck()) {
// Return 401 not authorized
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "User not logged in" };
throw new HttpResponseException(msg);
}
return base.ExecuteAsync(controllerContext, cancellationToken);
}
Following line of code gives me an error saying "The underlying connection was closed".
return this.repository.GetQuery<Countries>().Include(g => g.Cities).AsEnumerable().ToList();
But if I remove .Include(g => g.cities) it works fine.
this code is written in one of the operation in my WCF service, and I try to test it using WCF test client. I tried by calling this operation from MVC application also, and the same issue was occurring there too.
Also, i am using generic repository with entity framework
Repository code (only few important extract)
Constructor:
public GenericRepository(DbContext objectContext)
{
if (objectContext == null)
throw new ArgumentNullException("objectContext");
this._dbContext = objectContext;
this._dbContext.Configuration.LazyLoadingEnabled = false;
this._dbContext.Configuration.ProxyCreationEnabled = false;
}
GetQuery method:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
Attempt#1
Created following overloads in repository code:
public IQueryable<TEntity> GetQuery<TEntity>(params string[] includes) where TEntity : class
{
var entityName = GetEntityName<TEntity>();
IQueryable<TEntity> query = ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
foreach(string include in includes)
{
query = query.Include(include);
}
return query;
}
public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate, params string[] includes) where TEntity : class
{
return GetQuery<TEntity>(includes).Where(predicate);
}
WCF is now trying to execute following line of code:
return this.repository.GetQuery<Countries>("Cities").AsEnumerable().ToList()
But it still gives the same error of "The underlying connection was closed". I tested it in WCF test client. However, when I debug the repository code it shows the navigation object getting included in result, but the issue seems occurring while trying to pass the output to client (WCF test client, or any other client)
After looking at the code you've now posted, I can conclude that, indeed, your DbContext is being closed at the end of the GetQuery method, and is thus failing when you try to use include. What you might want to do to solve it is to have an optional params variable for the GetQuery method that will take in some properties to be included, and just do the include right in the GetQuery method itself.
When I raise an exception in my WCF service. The built in WebErrorHandler exception handler itself fails trying to access OperationContext.Current.IncomingMessageProperties as it seems to have been disposed. The consequence is that the real error is masked, and I always get an HTML error output.
Why would my IncomingMessageProperties be disposed? The service returns a stream - not sure if that is relevant.
I found the problem. I was creating a WebFaultException. Now I create a normal Exception and use IErrorHandler to serialize it correctly to the client.
This happen to me when i try to acces OperationContext.Current.IncomingMessageProperties from another method, not the OperationContract method.
To access OperationContext.Current.IncomingMessageProperties
create an extencion method to OperationContext to acces the property
something like this:
public static string GetClientIP(this OperationContext context)
{
string ip = "";
MessageProperties messageProperties = context.IncomingMessageProperties;
RemoteEndpointMessageProperty endpointProperty =
messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
if (endpointProperty != null)
{
ip = endpointProperty.Address;
}
return ip;
}
I have an operation handler that checks for authentication and throws an exception when authentication fails using
throw new WebFaultException(HttpStatusCode.Unauthorized);
However this still returns a 404 Not Found status code to the client/test client.
This is my operation handler
public class AuthOperationHandler : HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>
{
RequireAuthorizationAttribute _authorizeAttribute;
public AuthOperationHandler(RequireAuthorizationAttribute authorizeAttribute) : base("response")
{
_authorizeAttribute = authorizeAttribute;
}
protected override HttpRequestMessage OnHandle(HttpRequestMessage input)
{
IPrincipal user = Thread.CurrentPrincipal;
if (!user.Identity.IsAuthenticated)
throw new WebFaultException(HttpStatusCode.Unauthorized);
if (_authorizeAttribute.Roles == null)
return input;
var roles = _authorizeAttribute.Roles.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (roles.Any(role => user.IsInRole(role)))
return input;
throw new WebFaultException(HttpStatusCode.Unauthorized);
}
}
Am I doing something wrong?
I have good and bad news for you. The framework your are using has evolved into ASP.NET Web API. Unfortunately, OperationHandlers no longer exist. Their closest equivalent are ActionFilters.
Having said that, WCF Web API never supported throwing WebFaultException, that is a vestige of WCF's SOAP heritage. I think the exception was called HttpWebException, however, I never used it, I just set the status code on the response.