How do I config docfx to include C# event handler delegates in document? - docfx

I have some event handler delegates in my project, and I want to include them into my auto-generated document with docfx.
I know there is a filter option in docfx.json > metadata, but how could I config it? Any ideas?
I have try to add a filterConfig.yml, and modify the docfx.json, no any error, but this doesn't work.
C# code:
/// <summary>
/// Double click on one row of the search result, this should open the detail form of it
/// </summary>
/// <param name="sender">Default_GridView</param>
/// <param name="e">EventArgs</param>
private void Default_GridView_DoubleClick(object sender, EventArgs e)
{
//open the detail form here
}
docfx.json:
{
"metadata": [
{
"src": [
{
"files": [
"*.csproj"
],
"cwd": ".",
"exclude": [
"**/obj/**",
"**/bin/**",
"_site/**"
]
}
],
"dest": "obj/api",
"filter": "filterConfig.yml"
}
],
filterConfig.yml:
apiRules:
- include:
uidRegex: ^System\.EventHandler
type: Type

You will have to make your event handler method public, protected or protected internal. It is generally not possible to include private methods in your API documentation, which makes sense because private methods are not part of your API.
Once you did that, you can remove that API filter. It will just work.

Related

Properties Added with Aspects Not in Generated JSON Schema

I'm trying to generate JSON Schemas using Newtonsoft JSON Schema. Regular properties added in my POCO class are added to the schema when it is generated. I'm also using PostSharp Aspects to add properties, but none of those are added to the schema.
This is a Console application targeting .NET 4.7.2.
Here is my Console application:
public class Program
{
private static void Main(string[] args)
{
JSchemaGenerator gen = new JSchemaGenerator();
JSchema schema = gen.Generate(typeof(MyClass));
File.WriteAllText("C:\\Temp\\TestSchema.json", schema.ToString());
}
}
Here is my aspect:
[PSerializable]
public class TestAspect : InstanceLevelAspect
{
[IntroduceMember]
[JsonProperty(Required = Required.Always)]
public string AspectProp { get; set; }
}
And here is my POCO:
[TestAspect]
public class MyClass
{
public int MyProperty { get; set; }
}
Finally, here is the generated schema:
{
"type": "object",
"properties": {
"MyProperty": {
"type": "integer"
}
},
"required": [
"MyProperty"
]
}
The MyProperty property is in the schema, but AspectProp - the property added by the aspect - is not.
When I open the exe in a decompiler, I can see that AspectProp is actually added to MyClass:
I'm not sure if this is a problem with PostSharp or Newtonsoft JSON Schema or if I'm doing something wrong. It seems like this should be possible.
Edit 1: 20 May
I split my solution out into separate projects - one for the Console app, one for the Aspect and one for MyClass. After making sure I was referencing the generated MyClass DLL directly (i.e. not a project reference, I actually removed the project once MyClass was built) it did not make a difference. AspectProp is still not in the schema. Based on this and the serialization suggested below by #dbc, this leads me to believe it is a problem with the Newtonsoft schema generator
Edit 2: 20 May
Per Antonin's Answer below, I created a new ContractResolver:
public class AspectPropertyResolver : DefaultContractResolver
{
public AspectPropertyResolver()
{
SerializeCompilerGeneratedMembers = true;
}
}
And then registered it in my app before calling Generate:
gen.ContractResolver = new AspectPropertyResolver();
Now my schema includes the aspect-generated property.
Newtonsoft.Json has an opt-in feature to serialize compiler-generated properties. See Newtonsoft.Json.Serialization.DefaultContractResolver.SerializeCompilerGeneratedMembers property.

Access `DownstreamReRoute` within `DelegatingHandler` in Ocelot

Is it possible to access the DownstreamReRoute object within a DelegatingHandler in Ocelot? I have a DelegatingHandler instance and I'd like to slightly alter its behaviour based on the downstream reroute that is being called. I could do this based on the URL of the HttpRequestMessage that is passed into the SendAsync() method but I'd prefer to do it by the downstream reroute key, if possible.
I've had a look at the dependencies added by the OcelotBuilder to see if there's something I could inject to then query the current reroute but it doesn't seem possible.
E.g. If I have a DelegatingHandler
public class MyDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// access reRoute here?
}
}
and config (required fields removed for brevity)
"ReRoutes": [
{
"DownstreamPathTemplate": "/downstream/foo",
"UpstreamPathTemplate": "/foo",
"Key": "FooKey",
"DelegatingHandlers": [ "MyDelegatingHandler" ]
},
{
"DownstreamPathTemplate": "/downstream/bar",
"UpstreamPathTemplate": "/bar",
"Key": "BarKey",
"DelegatingHandlers": [ "MyDelegatingHandler" ]
}]
Is it possible to know whether I'm calling FooKey or BarKey reRoute from a delegatingHandler?
I managed to solve this by adding a PreQueryStringBuilderMiddleware handler when configuring Ocelot.
By doing this I could set a property in the HttpContext.Items collection that contains the reRouteKey. This can then be picked up by further processors in the request.
E.g. (without null handling etc)
app.UseOcelot(cfg =>
cfg.PreQueryStringBuilderMiddleware = (ctx, next) => {
ctx.HttpContext.Items["ReRouteKey"] = ctx.DownstreamReRoute.Key;
return next.Invoke();
});
I just found a solution to this, you must use IHttpContextAccessor.
On MyDelegatingHandler.cs
using Ocelot.Middleware;
public class MyDelegatingHandler : DelegatingHandler
{
private IHttpContextAccessor _httpContext;
public MyHandler(IHttpContextAccessor httpContext)
{
_httpContext = httpContext;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var downstreamRoute = _httpContext.HttpContext.Items?.DownstreamRouteHolder()?.Route?.DownstreamRoute?.FirstOrDefault();
if (downstreamRoute != null)
{
if(downstreamRoute.Key == "FooKey")
{
//Do something
} else if (downstreamRoute.Key == "BarKey")
{
//Do something else
}
}
}
}
You also need to register services.AddHttpContextAccessor(); on Startup.cs
NB: To be specific my config was "Routes" but not "ReRoutes". I believe it going to work for "ReRoutes" config too.

Distinguish applications emitting events to AI using Microsoft.Diagnostics.EventFlow

I have two services running in Azure Service Fabric. Now, i want to capture some events from both and redirect them to the same instance of AI.
My eventFlowConfig.json looks like this:
{
"inputs": [
{
"type": "EventSource",
"sources": [
{
"providerName": "My-Provider-Name"
}
]
}
],
"outputs": [
{
"type": "ApplicationInsights",
"instrumentationKey": "AI_INSTRUMENTATION_KEY"
}
],
"schemaVersion": "2016-08-11"
}
This works just fine. One problem though: in AI I cannot tell if a particular trace item came from service A or service B. So, i need a way to add some custom property to AI items coming from a particular app (service). Like, "Service A" or "Service B". Ideally, i should also be able to add PartiotionId & ReplicaOrInstanceId of that service instance, but that is a bonus without which i can live.
UPDATE:
As #cijothomas suggested, we can use an ITelemetryInitializer. This works because EventFlow uses TelemetryConfiguration.Active. We can do this in the constructor of our service:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new TelemetryInitializer("Service A", context.TraceId));
And here is the definition for the TelemetryInitializer class:
class TelemetryInitializer : ITelemetryInitializer
{
private readonly string _roleName;
private readonly string _roleInstance;
public TelemetryInitializer(string roleName, string roleInstance)
{
_roleName = roleName;
_roleInstance = roleInstance;
}
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = _roleName;
telemetry.Context.Cloud.RoleInstance = _roleInstance;
}
}
If same ikey is used for multiple apps, they can be distinguished using their cloudRoleInstance. If its not auto-populated, then you can write own telemetry initializer to populate it correctly.
https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-filtering-sampling#add-properties-itelemetryinitializer
item.Context.Cloud.RoleInstance = "service A or B"

HttpContext.Current.Session Is Null In Global.asax

I used forms authentication.
In LdapAuthentication.cs I have property
public static string ReturnProject
{
get
{
return HttpContext.Current.Session["Project"].ToString();
}
}
In global.asax.cs I trying to get Session["Project"] from LdapAuthentication.cs for check and ridirect to other pages according with rusult in Session["Project"], but I've got System.NullReferenceException. I cheked Session["Project"] in LdapAuthentication.cs - is ok
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Request.AppRelativeCurrentExecutionFilePath == "~/")
{
if (LdapAuthentication.ReturnProject == "Team Leader")
HttpContext.Current.RewritePath("~/TLPage.aspx");
else
if (LdapAuthentication.ReturnName == "ccobserver")
HttpContext.Current.RewritePath("~/ScheduleReport.aspx");
else
HttpContext.Current.RewritePath("~/PersonPage.aspx");
}
}
doesn't matter which handler use Application_AcquireRequestState or Application_AuthenticateRequest.
Thanks!
You declared ReturnProject static property, while HttpContext is an instance property of HttpApplication class, implemented in Global.asax.
Static property has no access to instance properties, so removing static modifier from ReturnProject should fix the problem.
EDIT
Now I get it, ReturnProject property declared in LdapAuthentication.cs, not in Global.asax.
Let me explain a bit. Every time request hits a server, new instance of HttpApplication (hence, HttpContext instance property) is created. This way you get access to Request and Session — they are bound to concrete request associated with concrete user. Related answer: “HttpContext.Current.Session” vs Global.asax “this.Session”
What you seem trying to achieve is to pass some session variable (Project) to the LdapAuthentication class in a nice way. There are more than one way to do this, but the obvious way without looking to the source of LdapAuthentication class is to add LdapAuthentication instance field to the Global.asax, passing session variable through the constructor or property initializer. Something like this:
LdapAuthentication.cs
/// <summary>
/// Creates instance of LdapAuthentication with Project value initialized
/// </summary>
public LdapAuthentication(string project) {
this.ReturnProject = project;
}
Global.asax
private LdapAuthentication auth = new LdapAuthentication(
HttpContext.Current.Session["Project"].ToString()
);
...
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Request.AppRelativeCurrentExecutionFilePath == "~/")
{
if (auth.ReturnProject == "Team Leader")
HttpContext.Current.RewritePath("~/TLPage.aspx");
else
if (auth.ReturnName == "ccobserver")
HttpContext.Current.RewritePath("~/ScheduleReport.aspx");
else
HttpContext.Current.RewritePath("~/PersonPage.aspx");
}
}

Cross Origin Resource Sharing for c# WCF Restful web service hosted as Windows service

I have a WCF Restful service which I am hosting as Windows service. I want to add cross domain support to my service. However, i can do this easily when I use global.asax file. But I want to host my service as a windows service.
i have created a project which host my service as windows service. Now the problem I am facing is, I am not able to add cross domain support now. I tried all possible solutions I could find through app.config file, but none works. I have tried solutions on these links:
dotnet tricks
enable-cors.org
I tried setting the header in the code using the following function by calling it in each service contract method.
private static void SetResponseHeader()
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache, no-store");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Request-Methods", "GET, POST, PUT, DELETE, OPTIONS");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept");
}
Interface:
namespace ReaderService
{
[ServiceContract]
public interface INFCReader
{
[OperationContract]
[WebInvoke(UriTemplate = "GetLogin", Method = "POST")]
GetLoginResults GetLogin(DisplayRequest dispRequest);
}
Here DisplayRequest is a class.
Please help guys. Let me know if anybody want to have look at any other code.
Thanks a lot.
EDIT:::::::
Thanks a lot Thomas for your reply.
I created a class MessageInspector which implement IDispactchMessageInspector. I have following code in MessageInspector class.
public class MessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
return null;
}
}
The error that I am getting now is -- 'Object reference not set to an instance of an object.'
The error is at this line of the above code
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
All I want to do is add CORS support to my web service. Please let me know if I am doing it correctly. OR is there any other way to do the same.
Finally found a solution to my queries.
Its all here. Supporting Cross Origin Resource
Nice step by step explanation. I guess I could have never figured this out on my own.
CODE:
Create 2 classes as follows:
MessageInspector implementing IDispatchMessageInspector.
BehaviorAttribute implementing Attribute, IEndpointBehavior and IOperationBehavior.
With the following details:
//MessageInspector Class
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
namespace myCorsService
{
public class MessageInspector : IDispatchMessageInspector
{
private ServiceEndpoint _serviceEndpoint;
public MessageInspector(ServiceEndpoint serviceEndpoint)
{
_serviceEndpoint = serviceEndpoint;
}
/// <summary>
/// Called when an inbound message been received
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="channel">The incoming channel.</param>
/// <param name="instanceContext">The current service instance.</param>
/// <returns>
/// The object used to correlate stateMsg.
/// This object is passed back in the method.
/// </returns>
public object AfterReceiveRequest(ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
StateMessage stateMsg = null;
HttpRequestMessageProperty requestProperty = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
{
requestProperty = request.Properties[HttpRequestMessageProperty.Name]
as HttpRequestMessageProperty;
}
if (requestProperty != null)
{
var origin = requestProperty.Headers["Origin"];
if (!string.IsNullOrEmpty(origin))
{
stateMsg = new StateMessage();
// if a cors options request (preflight) is detected,
// we create our own reply message and don't invoke any
// operation at all.
if (requestProperty.Method == "OPTIONS")
{
stateMsg.Message = Message.CreateMessage(request.Version, null);
}
request.Properties.Add("CrossOriginResourceSharingState", stateMsg);
}
}
return stateMsg;
}
/// <summary>
/// Called after the operation has returned but before the reply message
/// is sent.
/// </summary>
/// <param name="reply">The reply message. This value is null if the
/// operation is one way.</param>
/// <param name="correlationState">The correlation object returned from
/// the method.</param>
public void BeforeSendReply(ref Message reply, object correlationState)
{
var stateMsg = correlationState as StateMessage;
if (stateMsg != null)
{
if (stateMsg.Message != null)
{
reply = stateMsg.Message;
}
HttpResponseMessageProperty responseProperty = null;
if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name))
{
responseProperty = reply.Properties[HttpResponseMessageProperty.Name]
as HttpResponseMessageProperty;
}
if (responseProperty == null)
{
responseProperty = new HttpResponseMessageProperty();
reply.Properties.Add(HttpResponseMessageProperty.Name,
responseProperty);
}
// Access-Control-Allow-Origin should be added for all cors responses
responseProperty.Headers.Set("Access-Control-Allow-Origin", "*");
if (stateMsg.Message != null)
{
// the following headers should only be added for OPTIONS requests
responseProperty.Headers.Set("Access-Control-Allow-Methods",
"POST, OPTIONS, GET");
responseProperty.Headers.Set("Access-Control-Allow-Headers",
"Content-Type, Accept, Authorization, x-requested-with");
}
}
}
}
class StateMessage
{
public Message Message;
}
}
//BehaviorAttribute Class
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace OpenBetRetail.NFCReaderService
{
public class BehaviorAttribute : Attribute, IEndpointBehavior,
IOperationBehavior
{
public void Validate(ServiceEndpoint endpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
/// <summary>
/// This service modify or extend the service across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that exposes the contract.</param>
/// <param name="endpointDispatcher">The endpoint dispatcher to be
/// modified or extended.</param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
// add inspector which detects cross origin requests
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new MessageInspector(endpoint));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime) { }
public void Validate(OperationDescription operationDescription) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation) { }
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation) { }
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters) { }
}
}
After this all you need to do is add this message inspector to service end point behavior.
ServiceHost host = new ServiceHost(typeof(myService), _baseAddress);
foreach (ServiceEndpoint EP in host.Description.Endpoints)
EP.Behaviors.Add(new BehaviorAttribute());
Thanks guys for your help.
I believe that the closest thing to Application_BeginRequest in the WCF world are Message Inspectors:
A message inspector is an extensibility object that can be used in the service model's client runtime and dispatch runtime programmatically or through configuration and that can inspect and alter messages after they are received or before they are sent.
In order to use custom message inspectors you'll have to:
Create a class that implements the IDispatchMessageInspector interface (your custom inspector)
Add the custom inspector to your service's DispatchRuntime.MessageInspectors collection
Here you can find more info and some sample code on how to do this.

Resources