Mini-Profiler add external timings - mvc-mini-profiler

I'm using an MVC 3 application that uses business logic that contains timings in the response.
Now I would like to add these timings to the Mini-Profiler in my MVC3 app on a specific level.
I have tried it like this:
profiler.StepStart("key", "value");
var response = GetXXXResponse(request);
if (response != null && response.Timings)
{
foreach (var child in response.Timings)
{
var profiler = new MiniProfiler(child.Name, _profilerService.Profiler.Level;
profiler.ClientTimings = new ClientTimings { Timings = new List<ClientTimings.ClientTiming> { new ClientTimings.ClientTiming { Duration = child.ResponseTime, Name = child.Name, Start = 0 } } };
_profilerService.Profiler.AddProfilerResults(profiler);
}
}
profiler.StepStop("key");
I'm using Mini-Profiler version 2.0.2, in the UI I see the steps being added, but the timings are incorrect.
How can I Profile in MVC a specific function and add extra timing information for that block provided by the external process that cannot be changed?

Related

Why does Swagger need a version requestparameter, when the API version is in the URL?

I have implemented Swagger using Swashbuckle and MultipleApiVersions and it works like a charm. But I find it a bit ugly that the current setup requires a api-version request parameter. I assumed the version could be determined by the url /api/V1/Test.
How do I remove the api-version parameter and instruct swagger to base the version on the URL?
private static void SetupApiVersioningAndSwagger(IAppBuilder builder, AutofacWebApiDependencyResolver resolver)
{
// we only need to change the default constraint resolver for services that want urls with versioning like: ~/v{version}/{controller}
var constraintResolver = new DefaultInlineConstraintResolver() { ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) } };
var configuration = new HttpConfiguration();
configuration.DependencyResolver = resolver;
var httpServer = new HttpServer(configuration);
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
configuration.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
});
configuration.MapHttpAttributeRoutes(constraintResolver);
// add the versioned IApiExplorer and capture the strongly-typed implementation (e.g. VersionedApiExplorer vs IApiExplorer)
// note: the specified format code will format the version as "'v'major[.minor][-status]"
var apiExplorer = configuration.AddVersionedApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
configuration.EnableSwagger(
"{apiVersion}/swagger",
swagger =>
{
// build a swagger document and endpoint for each discovered API version
swagger.MultipleApiVersions(
(apiDescription, version) => apiDescription.GetGroupName() == version,
info =>
{
foreach (var group in apiExplorer.ApiDescriptions)
{
var description = string.Empty;
if (#group.IsDeprecated)
{
description += "This API version has been deprecated.";
}
info.Version(#group.Name, $"Force Search API v{#group.ApiVersion}")
.Description(description);
}
});
swagger.UseFullTypeNameInSchemaIds();
})
.EnableSwaggerUi(swagger => swagger.EnableDiscoveryUrlSelector());
builder.UseWebApi(httpServer);
}
The reason this is happening is because the default IApiVersionReader is a composition of both the query string and URL segment methods. This allows you to use either approach without any additional configuration. The reader implementations also describe where and how an API version is consumed so that it can be reported as a parameter. Since there are 2 readers configured at different locations, the result is 2 parameters. This is generally not a problem until you integrate OpenAPI/Swagger.
The default configuration looks like this:
configuration.AddApiVersioning(
options => options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader(),
new UrlSegmentApiVersionReader()));
Solution
Update your configuration as follows:
configuration.AddApiVersioning(
options =>
{
options.ReportApiVersions = true;
options.DefaultApiVersion = new ApiVersion(1, 0); // NOTE: already the default
options.ApiVersionReader = new UrlSegmentApiVersionReader();
options.AssumeDefaultVersionWhenUnspecified = true;
});
Afterward, there will be only a single parameter. Since you've configured options.SubstituteApiVersionInUrl = true, the net result will be zero API version parameters because the value is baked directly into the generated API description URL.

Various errors using VisionServiceClient in XamarinForms

I am trying to create a simple Xamarin forms app which allows the user to browse for or take a photo and have azure cognitive services tag the photo using a custom vision model.
I am unable to get the client to successfully authenticate or find a resource per the error message in the exception produced by the VisionServiceClient. Am I missing something? What would be the correct values to use for the arguments to VisionServiceClient?
All keys have been removed from the below images, they are populated.
Exception thrown in VS2017:
'Microsoft.ProjectOxford.Vision.ClientException' in System.Private.CoreLib.dll
Call to VisionServiceClient:
private const string endpoint = #"https://eastus2.api.cognitive.microsoft.com/vision/prediction/v1.0";
private const string key = "";
VisionServiceClient visionClient = new VisionServiceClient(key, endpoint);
VisualFeature[] features = { VisualFeature.Tags, VisualFeature.Categories, VisualFeature.Description };
try
{
AnalysisResult temp = await visionClient.AnalyzeImageAsync(imageStream,
features.ToList(), null);
return temp;
}
catch(Exception ex)
{
return null;
}
VS Exception Error:
Azure Portal for cognitive services:
Custom Vision Portal:
It looks like you're confusing the Computer Vision and the Custom Vision APIs. You are attempting to use the client SDK for the former using the API key of the latter.
For .NET languages, you'll want the Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction NuGet package.
Your code will end up looking something like this:
ICustomVisionPredictionClient client = new CustomVisionPredictionClient()
{
ApiKey = PredictionKey,
Endpoint = "https://southcentralus.api.cognitive.microsoft.com"
};
ImagePrediction prediction = await client.PredictImageAsync(ProjectId, stream, IterationId);
Thank you to cthrash for the extended help and talking with me in chat. Using his post along with a little troubleshooting I have figured out what works for me. The code is super clunky but it was just to test and make sure I'm able to do this. To answer the question:
Nuget packages and classes
Using cthrash's post I was able to get both the training and prediction nuget packages installed, which are the correct packages for this particular application. I needed the following classes:
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction.Models
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.Models
Endpoint Root
Following some of the steps Here I determined that the endpoint URL's only need to be the root, not the full URL provided in the Custom Vision Portal. For instance,
https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/
Was changed to
https://southcentralus.api.cognitive.microsoft.com
I used both the key and endpoint from the Custom Vision Portal and making that change I was able to use both a training and prediction client to pull the projects and iterations.
Getting Project Id
In order to use CustomVisionPredictionClient.PredictImageAsync you need a Guid for the project id and an iteration id if a default iteration is not set in the portal.
I tested two ways to get the project id,
Using project id string from portal
Grab the project id string from the portal under the project settings.
For the first argument to PredictImageAsync pass
Guid.Parse(projectId)
Using the training client
Create a new CustomVisionTrainingClient
To get a list of <Project> use
TrainingClient.GetProjects().ToList()
In my case I only had a single project so I would just need the first element.
Guid projectId = projects[0].Id
Getting Iteration Id
To get the iteration id of a project you need the CustomVisionTrainingClient.
Create the client
To get a list of <Iteration> use
client.GetIterations(projectId).ToList()
In my case I had only a single iteration so I just need the first element.
Guid iterationId = iterations[0].Id
I am now able to use my model to classify images. In the code below, fileStream is the image stream passed to the model.
public async Task<string> Predict(Stream fileStream)
{
string projectId = "";
//string trainingEndpoint = "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.2/Training/";
string trainingEndpoint = "https://southcentralus.api.cognitive.microsoft.com/";
string trainingKey = "";
//string predictionEndpoint = "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/";
string predictionEndpoint = "https://southcentralus.api.cognitive.microsoft.com";
string predictionKey = "";
CustomVisionTrainingClient trainingClient = new CustomVisionTrainingClient
{
ApiKey = trainingKey,
Endpoint = trainingEndpoint
};
List<Project> projects = new List<Project>();
try
{
projects = trainingClient.GetProjects().ToList();
}
catch(Exception ex)
{
Debug.WriteLine("Unable to get projects:\n\n" + ex.Message);
return "Unable to obtain projects.";
}
Guid ProjectId = Guid.Empty;
if(projects.Count > 0)
{
ProjectId = projects[0].Id;
}
if (ProjectId == Guid.Empty)
{
Debug.WriteLine("Unable to obtain project ID");
return "Unable to obtain project id.";
}
List<Iteration> iterations = new List<Iteration>();
try
{
iterations = trainingClient.GetIterations(ProjectId).ToList();
}
catch(Exception ex)
{
Debug.WriteLine("Unable to obtain iterations.");
return "Unable to obtain iterations.";
}
foreach(Iteration itr in iterations)
{
Debug.WriteLine(itr.Name + "\t" + itr.Id + "\n");
}
Guid iteration = Guid.Empty;
if(iterations.Count > 0)
{
iteration = iterations[0].Id;
}
if(iteration == Guid.Empty)
{
Debug.WriteLine("Unable to obtain project iteration.");
return "Unable to obtain project iteration";
}
CustomVisionPredictionClient predictionClient = new CustomVisionPredictionClient
{
ApiKey = predictionKey,
Endpoint = predictionEndpoint
};
var result = await predictionClient.PredictImageAsync(Guid.Parse(projectId), fileStream, iteration);
string resultStr = string.Empty;
foreach(PredictionModel pred in result.Predictions)
{
if(pred.Probability >= 0.85)
resultStr += pred.TagName + " ";
}
return resultStr;
}

Multiple ajax get requests of same route but different query string

I have had this for a couple of days now.
I have a simple search form. When form is submitted the server searches for some data from another server and return data to the screen. When the submit completes it gets some javascript from the server based on the results returned from the search. the javascript then makes multiple concurrent jquery get requests, lets say 4, to the asp.net mvc3 webapp.
I have demonstrated that all the get requests fire at the same time in Firebug but when debugging my app with VS the breakpoints only get hit once the previous request completes.
The actions are the same but the querys are different; ie
/Home/Details/040801
/Home/Details/040802
/Home/Details/040803
So these are different URLs and, from what i found out, FF should treat them differently.
So my questions are:
Am I missing something obvious?
Does IIS have some funny blocking on the same route?
Is it a session cache issue? I am locking lock (lockobject){} on writes to the common session variables.
Im not using ViewBag or TempData.
The page load times, even when everything is cached in the Session, are still noticeably synchronous.
Windows Server 2008 R2
Using IIS 7.5
ASP.NET MVC 3
VS2010 Chrome or FF browser
I have my routes set up as follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
routes.MapRoute("Details", "{controller}/{action}/{id}/{booking}", new { id = UrlParameter.Optional, booking = UrlParameter.Optional});
Nothing special there as you can see.
Sample code from one of the blocked routes:
public ActionResult Details(string id, bool booking = false)
{
if (booking)
{
return BookingDetails(id, true);
}
Dictionary<string, FlightDetails> detailDic;
string scenarioInput;
lock (DetailsLock)
{
if (Session["DetailDic"] == null)
{
Session["DetailDic"] = new Dictionary<string, FlightDetails>();
}
detailDic = (Dictionary<string, FlightDetails>)Session["DetailDic"];
}
if (detailDic.ContainsKey(id))
{
return PartialView("Details", detailDic[id]);
}
lock (GuidLock)
{
if (Session["DetailGuids"] == null)
{
Session["DetailGuids"] = new Dictionary<string, string>();
}
scenarioInput = ((Dictionary<string, string>)Session["DetailGuids"])[id];
}
// query results list
string queryText = string.Format("<View><Query><Where><Eq><FieldRef Name=\"Title\" /><Value Type=\"Text\">OUT {0}</Value></Eq></Where></Query></View>", scenarioInput);
ListItemCollection oList;
int counter = 0;
do
{
oList = SharepointHelper.GetListFromSharepoint("ListName", queryText, ClientContext);
counter++;
Thread.Sleep(1000);
} while (oList.Count == 0 && counter <= Timeout);
if (oList.Count == 0)
{
return PartialView("Details", (object)null);
}
var item = oList[0];
FlightDetails flightDetails = CreateFlightDetails(id, scenarioInput, item);
lock (DetailsLock)
{
detailDic.Add(id, flightDetails);
}
return PartialView("Details", flightDetails);
}
when using session object in server-side your async calls wait for session object released by other request. Becuase of this async ajax calls act like sync. You have to use session as readonly in that action.
Add this attribute to action you call if you dont write anything to session.
[SessionState(SessionStateBehavior.ReadOnly)]

Getting Web Site Name from Web Project Setup

I'm creating a setup project for WCF net-tcp service. One thing I came across is that I need to change "Web Site->Manage Application->Advanced settings->Enabled Protocols". It can be also done using command line:
%windir%\system32\inetsrv\appcmd.exe set app "[Web Site Name]/[Applicaiton Name]" /enabledProtocols:http,net.tcp
The problem is in custom action I can get [TARGETSITE] but it's value is "/LM/W3SVC/2" (I have [TARGETVDIR] too). The question is how can I get Web Site Name or how can I use [TARGETSITE] to set application enabled protocols?
The solution I ended with involves converting metabasePath to site name and then using appcmd:
private static string GetSiteName(string metabasePath)
{
var siteIdString = metabasePath.Substring(metabasePath.LastIndexOf("/") + 1);
long siteId;
long.TryParse(siteIdString, out siteId);
if (siteId != 0)
{
var iisManager = new ServerManager();
var config = iisManager.GetApplicationHostConfiguration();
var sites = config.GetSection("system.applicationHost/sites").GetCollection();
ConfigurationElement selectedSite = null;
foreach (var site in sites)
{
if ((long)site.GetAttribute("id").Value == siteId)
selectedSite = site;
}
if (selectedSite != null)
{
return selectedSite.GetAttribute("name").Value as string;
}
}
return null;
}
To use this you will have to reference:
C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll
C:\Windows\System32\inetsrv\Microsoft.Web.Management.dll

Windows Workflow Foundation 4.0 and Tracking

I'm working with the Beta 2 version of Visual Studio 2010 to get some advanced learning using WF4. I've been working with the SqlTracking Sample in the WF_WCF_Samples SDK, and have gotten a pretty good understanding of how to emit and store tracking data in a SQL Database, but haven't seen anything on how to query the data when needed. Does anyone know if there are any .Net classes that are to be used for querying the tracking data, and if so are there any known samples, tutorials, or articles that describe how to query the tracking data?
According to Matt Winkler, from the Microsoft WF4 Team, there isn't any built in API for querying the tracking data, the developer must write his/her own.
These can help:
WorkflowInstanceQuery Class
Workflow Tracking and Tracing
Tracking Participants in .NET 4 Beta 1
Old question, I know, but there is actually a more or less official API in AppFabric: Windows Server AppFabric Class Library
You'll have to find the actual DLL's in %SystemRoot%\AppFabric (after installing AppFabric, of course). Pretty weird place to put it.
The key classes to look are at are SqlInstanceQueryProvider, InstanceQueryExecuteArgs. The query API is asynchronous and can be used something like this (C#):
public InstanceInfo GetWorkflowInstanceInformation(Guid workflowInstanceId, string connectionString)
{
var instanceQueryProvider = new SqlInstanceQueryProvider();
// Connection string to the instance store needs to be set like this:
var parameters = new NameValueCollection()
{
{"connectionString", connectionString}
};
instanceQueryProvider.Initialize("Provider", parameters);
var queryArgs = new InstanceQueryExecuteArgs()
{
InstanceId = new List<Guid>() { workflowInstanceId }
};
// Total ruin the asynchronous advantages and use a Mutex to lock on.
var waitEvent = new ManualResetEvent(false);
IEnumerable<InstanceInfo> retrievedInstanceInfos = null;
var query = instanceQueryProvider.CreateInstanceQuery();
query.BeginExecuteQuery(
queryArgs,
TimeSpan.FromSeconds(10),
ar =>
{
lock (synchronizer)
{
retrievedInstanceInfos = query.EndExecuteQuery(ar).ToList();
}
waitEvent.Set();
},
null);
var waitResult = waitEvent.WaitOne(5000);
if (waitResult)
{
List<InstanceInfo> instances = null;
lock (synchronizer)
{
if (retrievedInstanceInfos != null)
{
instances = retrievedInstanceInfos.ToList();
}
}
if (instances != null)
{
if (instances.Count() == 1)
{
return instances.Single();
}
if (!instances.Any())
{
Log.Warning("Request for non-existing WorkflowInstanceInfo: {0}.", workflowInstanceId);
return null;
}
Log.Error("More than one(!) WorkflowInstanceInfo for id: {0}.", workflowInstanceId);
}
}
Log.Error("Time out retrieving information for id: {0}.", workflowInstanceId);
return null;
}
And just to clarify - this does NOT give you access to the tracking data, which are stored in the Monitoring Database. This API is only for the Persistence Database.

Resources