Background
I have a web api project which uses complex types for GET requests, here is an example of a controller method, and its associated complex type
[RoutePrefix("api")]
public class MyController : ApiController
{
[Route("Something")]
public IHttpActionResult GetSomething([FromUri]RequestObject request)
{
// do something to get "data"
return Ok(data);
}
}
// elsewhere
public class RequestObject
{
[Required]
public string SomeValue{get;set;}
}
This works with a url such as http://domain.com/api/Something?SomeValue=foo.
I would like to use alias' for these parameters, for which I will do some complex stuff (once I have this working) but effectively I have defined an attribute AliasAttribute.
[AttributeUsage(AttributeTargets.Property,AllowMultiple=true)]
public class AliasAttribute : Attribute
{
public string ParameterName { get; private set; }
public AliasAttribute(string parameterName)
{
this.ParameterName = parameterName;
}
}
Which I would like to adorn onto my request model like so:
// elsewhere
public class RequestObject
{
[Required,Alias("val")]
public string SomeValue{get;set;}
}
Allowing my url to shorten to http://domain.com/api/Something?val=foo. This is a contrived and simplified example, but hopefully demonstrates the background.
Problem
ModelBinding in web api has become very complex compared to Mvc model binding. I am getting twisted up between IModelBinder, IValueProvider, HttpParameterBinding et al.
I would like an example of where I should hook in to the model binding to allow me to write the value to my model from the querystring - note that I only use this aliasing behaviour when the route uses the FromUri attribute (see MyController.GetSomething above).
Question title: Support aliased arguments in get requests for web api. I think you are re-inventing a wheel here AliasAttribute , and have not given a really good reason why you don't want to use community ways of doing this.
I have done something similar with Newtonsoft.Json serializer. But if you want something ootb I'd have to google around.
public class RequestObject
{
[Required]
[JsonProperty("vla")]
public string SomeValue{get;set;}
}
Example SO that uses it: .Net NewtonSoft Json Deserialize map to a different property name
Here is a more agnostic way to do it.
[DataContract]
public class RequestObject
{
[DataMember(Name="val", IsRequired=true)]
public string SomeValue{get;set;}
}
Related
We have a POST action in our asp.net core application that accepts a dynamic object.
[HttpPost]
public Task<ActionResult> SubmitAsync(dynamic unitOfWork)
We'd like to transform this POST action to a gRPC server and we'd like to continue receiving dynamic objects in the gRPC service. What is the equivalent of C# dynamic definition in gRPC protobuf file definition? Or if that cannot be achieved what's the best way to receive a dynamic object?
That isn't really a thing right now. In protobuf terms, Any is the closest thing, but I have not yet implemented that in protobuf-net (it is on my short term additions list). The legacy "dynamic types" feature in protobuf-net (that sends type metadata) is actively being phased out, with Any being the preferred route since it allows cross-platform usage and doesn't have the same metadata dependencies.
Frankly, though, I'd probably say "just don't do this"; instead, prefer oneof; it isn't likely that you actually mean "anything" - you probably just mean "one of these things that I expect, but I don't know which", and oneof expresses that intent. More: protobuf-net implements inheritance via oneof, so a good option is something like:
[ProtoContract]
[ProtoInclude(1, typeof(FooRequest))]
[ProtoInclude(2, typeof(BarRequest))]
public abstract class RequestBase {}
[ProtoContract]
public class FooRequest {}
[ProtoContract]
public class BarRequest {}
You can pass messages with fields whose type was not known in advance. You can also pass messages with fields that are not typed, such as dynamic objects that can take any scalar values, and collections null values are allowed.
To do so, import the proto file "google/protobuf/struct.proto" and declare the
dynamic type as google.protobuf.Value.
So, first add bellow line at the top of your proto file:
import "google/protobuf/struct.proto";
Here my sample message with two dynamic fields:
message BranchResponse {
google.protobuf.Value BranchId = 1;
google.protobuf.Value BranchLevel = 2;
}
Note that: the generated type in C# is Value and belongs to the Google.Protobuf.WellKnownTypes namespace, which belongs itself to the Google.Protobuf assembly. This type inherits from the IMessage, IMessage, IEquatable, IDeepCloneable, and IBufferMessage interfaces that all belong to the Google.Protobuf assembly, except for IEquatable, which comes from the .NET System.Runtime assembly. To write and read dynamic values, we have a set of methods available that shown bellow: (these are write static functions)
We can fill BranchResponse model like this:
var branch = new BranchResponse();
branch.BranchId = Value.ForNumber(1);
branch.BranchLevel = Value.ForStruct(new Struct
{
Fields = {
["LevelId"] = Value.ForNumber(1),
["LevelName"] = Value.ForString("Gold"),
["IsProfessional"] = Value.ForBool(true)}
});
The read Value type is straightforward. The Value type has a set of properties that exposes its value in the wanted type. (these are read static functions)
At the end, you need to read data from your response model like this:
Here my c# classes that my response model is supposed to bind to them.
public class BranchModel
{
public int BranchId { get; set; }
public LevelModel Level { get; set; }
}
public class LevelModel
{
public int LevelId{ get; set; }
public string LevelName{ get; set; }
public bool IsProfessional { get; set; }
}
Finally:
var branch = new BranchResponse(); // Received filled from a gRPC call
// Read
var branchModel = new BranchModel
{
BranchId = Convert.ToInt32(branch.BranchId.NumberValue),
Level= new LevelModel
{
LevelId = Convert.ToInt32(branchModel.Level.StructValue.
Fields["LevelId"].NumberValue),
LevelName = branchModel.Level.StructValue.
Fields["LevelName"].StringValue,
IsProfessional = branchModel.Level.StructValue.
Fields["IsProfessional"].BoolValue,
}
};
I'm setting a Property on Request.Properties inside a DelegatingHandler after I pluck some data out of a header on an incoming request to a Web API.
This all works fine. I can also access Request.Properties from within the controller as well as in my Action and Exception filters. However, I also need to access this data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places,
I can see HttpContext.Current from this class, and I can see the original header from here, so I guess I could pluck it out again, but since I have already done this and put it in the Properties it seems to make more sense to get it from there. However, I don't seem to have access to the Request.Properties from anywhere else.
If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
I also need to access [Request.Properties] data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places... However, I don't seem to have access to the Request.Properties from anywhere else. If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
You can get it from HttpContext.Current, though it is less than ideal. Keep in mind that if any other non-web applications consume the same business layer, then HttpContext.Current would be null. HttpContext.Current is only non-null when you are running in IIS, and an IIS thread is handling the execution of the request stack. If you ever plan to self-host the web api using OWIN without IIS, there will be no HttpContext.Current.
Personally, if the data really is important enough to be passed into the business layer to be logged, then I would just pass it to the business layer method:
public Task<HttpResponseMessage> SomeAction(SomeModel model) {
... other code
someBusinessLayerObject.SomeMethod(arg1, arg2, Request.Properties["myHeaderKey"]);
}
...If you need other values from Request.Properties, then you can just pass the whole dictionary to the methods that will end up using its values.
A third option if you are using an inversion of control container would be to add some kind of scoped object dependency class and put the data in there. Then constructor inject it into your business layer class:
public interface IHaveRequestData {
IDictionary<string, object> Properties { get; set; }
}
public class RequestData : IHaveRequestData {
public IDictionary<string, object> Properties { get; set; }
}
// ioc registration pseudocode
iocContainer.Register<IHaveRequestData, RequestData>(Lifetime
.WhateverYouNeedSoThatOneOfTheseGetsCreatedForEachWebRequest);
public class SomeController : ApiController {
private readonly IHaveRequestData RequestData;
public SomeController(IHaveRequestData requestData) {
RequestData = requestData;
}
public Task<HttpResponseMessage> SomeAction() {
// you may even be able to do this part in an action filter
RequestData.Properties = Request.Properties;
}
}
public class SomeBusinessLayerComponent {
private readonly IHaveRequestData RequestData;
private readonly ILog Log;
public SomeBusinessLayerComponent(IHaveRequestData requestData, ILog log) {
RequestData = requestData;
Log = log;
}
public Task SomeMethod() {
Log.Info(RequestData["myHeader"]);
}
}
I have a domain service running smooth, some expose functions that return generic lists of defined entity, but for some reason, I had add some common information so I created a generic object to wrap the collection with the extra information that I need return.
but when after made the change and try use the service in the client, the function don't show up in the context, I already search about it and what I found was attributes for generic IQueryable
my wrap class
public class Wrap<T>
{
public String commonProperty { get; set; }
public String anotherCommonProperty { get; set; }
public List<T> items { get; set; }
}
in my service domain
public Wrap<SomeClass> GetAll()
{
Wrap<SomeClass> myObject = new Wrap<SomeClass>();
myObject.items = new List<SomeClass>();
myObject.commonProperty = "some info";
myObject.anotherCommonProperty = "some info";
return myObject;
}
Maybe adding the [KnownType(typeof(SomeClass))] attribute in the Wrap<T> class, the problem is that you need to include one KnowType attribute for every class in your domain (this is because you are making a polymorphic service).
And adding the [ServiceKnownType(typeof(SomeClass))] in the GetAll method in the service (this is for wcf services I don't know if is valid for domain services).
WCF RIA domain services does not support generic entity types. IEnumerable<T> and IQueryable<T> are special cases.
Your method was ignored because it did not match supported method type.
Before changes GetAll was recognized as Query method. You can force that by adding attribute.
[Query]
public Wrap<SomeClass> GetAll()
Now it does not dissapear silently. But generates compile time error instead:
Type 'Wrap`1' is not a valid entity type. Entity types cannot be
generic.
I have the following scenario in my Silverlight 4 application:
public class TheViewModel
{
[Import()]
public TheChild Child { get; set; }
}
[Export()]
public class TheChild
{
[ImportingConstructor()]
public TheChild(String myName, IAmTheService service) { ... }
}
[Export(typeof(IAmTheService))]
public class TheService : IAmTheService
{
public void DoSomething(String theName);
}
As you can see, TheChild's constructor requires one imported parameter and one static value that is context-sensitive (has to be provided by the parent). The string value cannot come from AppSettings, configuration, etc. and can only be provided by the current instance of the parent class (TheViewModel in this case).
As a rule-of-thumb, I've always approached dependency-injection as follows:
Required dependencies are satisfied through constructor injection
Optional dependencies are satisfied through property injection
The "myName" parameter is required so I would prefer to set it through the constructor but given the way MEF works, I realize this may have to change.
Can you tell me how you've handled this scenario and your thoughts behind the solution?
You can specify a specific import contract in conjunction with [ImportingConstructor]. For example:
[Export()]
public class TheChild
{
[ImportingConstructor()]
public TheChild([Import("MyName")] String myName, IAmTheService service) { ... }
Given that, an export of a string decorated with [Export("MyName")] will be required and used to fulfill the dependency. Any of the [Import] specifications should work in this case (ie: importing a subclass by type, importing by name, etc).
Can you override the ToString function in a WCF DataContrat? Right now I have:
[DataContract]
public class Keyword
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
But it doesn't seem to work. Anyway to get this working?
I realize this is old but wanted to provide an answer since I just created a sample app for a coworker that used this idea. All of this work can be done on the consumer/test client side.
If you look at the code on the consumer/test client and, more specifically, the classes that are generated as part of the service reference, you will see the [DataContract] type you are interested in. In order to do this you should make sure that 'Show All Files' is selected. Drill down to the 'Reference.cs' class.
This is the top of my test class from Reference.cs:
namespace WebApplication1.UCCTestSvcRef {
using System.Runtime.Serialization;
using System;
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="UCCRecord", Namespace="http://schemas.datacontract.org/2004/07/UCCTest")]
[System.SerializableAttribute()]
public partial class UCCRecord : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
The important bits that you will need to use are the namespace and the partial class. To make use of these you simply have to create a new class in your test client of the same type, in the same namespace, and override the ToString() method.
Here is an example of how to do that from the newly created UCCRecord.cs file on the consumer/test client.
namespace WebApplication1.UCCTestSvcRef
{
public partial class UCCRecord
{
public override string ToString()
{
return "Key: " + Key.ToString() + ", Time: " + Timestamp.ToString("d") + ", Value: " + Value;
}
}
}
Note that I can only reference Key and Timestamp and Value because they are [DataMember] values for the [DataContract].
This is relatively simple if you know what you are looking for. If anything here is not clear, please let me know and I will attempt to clarify.
Thanks
Where do you want to be able to invoke ToString()? Methods are not part of the DataContract so they won't be available when you create the proxy for the client.
Of course, nothing is stopping you from coding that method in the client's proxy yourself.
Remember too that if you own both the server and the client, that often you can use a shared library for data contracts rather than generating a client proxy. If you do that, then you can have the same method on both the server and client as they're exactly the same type.