I'm using
https://github.com/OpenAPITools/openapi-generator
to create a client for my API. It's mostly working ok, but the generator creates a lot of InlineObject types that encapsulate parameters that include types of any complexity, e.g Dictionary, IFormFile, Stream
For example,
public async Task<ApiResponse<FileUploadResponseAPIModel>> MyApiClientMethod(InlineObject11 inlineObject11 = default(InlineObject11))
{
}
Where InlineObject11 is defined as
public partial class InlineObject11 : IEquatable<InlineObject11>, IValidatableObject
{
[JsonConstructorAttribute]
protected InlineObject11() { }
public InlineObject11(Stream rebalanceTradeFile = default(Stream))
{
// to ensure "rebalanceTradeFile" is required (not null)
this.RebalanceTradeFile = rebalanceTradeFile ?? throw new ArgumentNullException("rebalanceTradeFile is a required property for InlineObject11 and cannot be null");
}
[DataMember(Name = "RebalanceTradeFile", IsRequired = true, EmitDefaultValue = false)]
public System.IO.Stream RebalanceTradeFile { get; set; }
What is the point of this? Why isn't the client generated to take Stream rebalanceTradeFile instead of wrapping it into an InlineObject? How can I fix it? This is breaking a bunch of my tools that use older versions of the generated client.
Inline Object is created from inline schema, e.g. https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/test/resources/3_0/inline_model_resolver.yaml#L15-L24 is a schema with 2 properties defined inline for the payload with the MIME type application/json.
To avoid OpenAPI Generator automatically generating a model for the inline schema, one can define the model separately (example) and use $ref instead:
application/json:
schema:
$ref: '#/components/schemas/Pet'
Using title field in schema object, the model name is title name instead of InlineObject.
Related
When using a FindOne() using MongoDB and C#, is there a way to ignore fields not found in the object?
EG, example model.
public class UserModel
{
public ObjectId id { get; set; }
public string Email { get; set; }
}
Now we also store a password in the MongoDB collection, but do not want to bind it to out object above. When we do a Get like so,
var query = Query<UserModel>.EQ(e => e.Email, model.Email);
var entity = usersCollection.FindOne(query);
We get the following error
Element 'Password' does not match any field or property of class
Is there anyway to tell Mongo to ignore fields it cant match with the models?
Yes. Just decorate your UserModel class with the BsonIgnoreExtraElements attribute:
[BsonIgnoreExtraElements]
public class UserModel
{
public ObjectId id { get; set; }
public string Email { get; set; }
}
As the name suggests, the driver would ignore any extra fields instead of throwing an exception. More information here - Ignoring Extra Elements.
Yet Another possible solution, is to register a convention for this.
This way, we do not have to annotate all classes with [BsonIgnoreExtraElements].
Somewhere when creating the mongo client, setup the following:
var pack = new ConventionPack();
pack.Add(new IgnoreExtraElementsConvention(true));
ConventionRegistry.Register("My Solution Conventions", pack, t => true);
Yes. Another way (instead of editing you model class) is to use RegisterClassMap with SetIgnoreExtraElements.
In your case just add this code when you initialize your driver:
BsonClassMap.RegisterClassMap<UserModel>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
You can read more about ignoring extra elements using class mapping here - Ignoring Extra Elements.
We have a set of REST services. The services accept models in json in the POST request, and return models in json in the POST response. These models however are not physically compiled types in our ASP.NET Core 2.0 projects. Instead, they are mapped objects that map on to our internal types. I.e. the json that is served up is merely a layer over the top of our core entities which are cut down for the purpose of exposing the data to 3rd parties.
However the entities need to be documented with Swagger. Swashbuckle does a nice job of this. It does so via the namespace Microsoft.AspNetCore.Mvc.ApiExplorer. I'm now trying to define metadata for the entities served up in json with this namespace. However, I'm not having much luck. I want to define a type that is not based on a physical type in my project. I am happy to define the name and the properties etc. but I can't get anywhere with what I've done. For example, the abstract class ModelMetadata requires an argument of type ModelMetadataIdentity, but this type doesn't take any arguments in the constructor, and all of the important properties are Get only. So, for example, I literally cannot set the Name property on ModelMetadataIdentity. I'm guessing that it's a glitch in the code that I can even construct a ModelMetadataIdentity. I'm guessing that the class was meant to be abstract.
There is for example a static method called ForType which compiles, and doesn't throw any exceptions, like so:
var customModelMetadataProvider = new CustomModelMetadataProvider(ModelMetadataIdentity.ForType(typeof(TaskInfo)));
context.ApiDescription.SupportedResponseTypes.Add(new Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType { ModelMetadata = customModelMetadataProvider });
But, this does nothing. No documentation appears for TaskInfo in the Swagger documentation. But, more importantly, this is no good to me because TaskInfo is precompiled type, and I am trying to define a conceptual type, not a physical type.
To get an idea of what I am talking about, you can look at a sample here:
https://github.com/Microsoft/aspnet-api-versioning/tree/master/samples/webapi/SwaggerODataWebApiSample . For example, Order, and Person are defined as types in the project, but I want to construct the meta data for these types dynamically.
How can I define a conceptual (dynamic) type using the Microsoft.AspNetCore.Mvc.ApiExplorer namespace? How do I force Swashbuckle to recognize metadata that I have defined using this namespace?
PS: I know that some people are going to be thinking "Why wouldn't you just write the models in code and compile them?". Well, obviously this could be done, but this adds an extra step to configuring the REST services that should not be necessary. The mapping from our internal entities to external entities is done with configuration - not code! A configuration person shouldn't have to compile anything to expose these entities.
This can be done with Swashbuckle like this:
public class SwaggerOperationFilter : IOperationFilter
{
#region Fields
private const string testpropertyname = "TestProperty";
private const string TestSchemaRef = "ADef";
private static Schema TestSchema = new Schema { Required = new List<string> { testpropertyname }, Example = new { TestProperty = "Test" }, Description = "This is a Description", Title = "TestSchema", Properties = new Dictionary<string, Schema>() };
#endregion
#region Static Constructor
static SwaggerOperationFilter()
{
TestSchema.Properties.Add(testpropertyname, new Schema { Type = "string" });
}
#endregion
#region Implementation
public void Apply(Operation operation, OperationFilterContext context)
{
if (!context.SchemaRegistry.Definitions.ContainsKey(TestSchemaRef))
{
context.SchemaRegistry.Definitions.Add(TestSchemaRef, TestSchema);
}
operation.Responses["200"] = new Response
{
Description = "This is a Response Description",
Schema = TestSchema
};
}
#endregion
}
I am having a hard time finding good detail on NEST 2.0, the wrapper for Elasticsearch 2.2 I am using. My question is this: Can I do a bulk insert on an object (class with public data members) and map that to Elasticsearch where only the mapped fields between my C# class and the ES server mapping will save? And it will not add the additional fields in my class I do not want?
Right now I have a class of with strings and doubles and lists of other classes in it. I want to save the strings and doubles, but NOT include the Lists on my bulk inserts to Elasticsearch. It wants to save every piece of data in every field of my class. Is there a class member attribute or some other way to say "do not add this field if it has no mapping" that I have missed? I hope so.
You can ignore properties of your POCO in a number of ways with NEST 2.x. Let's use the following POCO as an example
using Nest;
using Newtonsoft.Json;
[ElasticsearchType(Name = "company")]
public class Company
{
public string Name { get; set; }
[String(Ignore = true)]
public string IgnoreViaAttribute { get; set; }
public string IgnoreViaSettings { get;set; }
[JsonIgnore]
public string IgnoreViaSerializerSpecificAttribute { get; set; }
}
1.Using the Ignore property on a derived ElasticsearchPropertyAttribute type (in our example, StringAttribute on IgnoreViaAttribute property) applied to the property that should be ignored on the POCO
2.Using the .InferMappingFor<TDocument>(Func<ClrTypeMappingDescriptor<TDocument>, IClrTypeMapping<TDocument>> selector) on the connection settings
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.InferMappingFor<Company>(i => i
.Ignore(p => p.IgnoreViaSettings)
);
var client = new ElasticClient(settings);
These settings are cached per instance of ConnectionSettings.
3.Using an ignore attribute applied to the POCO property that is understood by the IElasticsearchSerializer used, and inspected inside of the CreatePropertyMapping() on the serializer. In the case of the default JsonNetSerializer, this is the Json.NET JsonIgnoreAttribute. In our example, this is demonstrated by the attribute applied to the IgnoreViaSerializerSpecificAttribute property.
What I found by digging around a bit and testing a small class is that the following structure did indeed hide the attributes when you post a class with NEST 2.0 C#. If you do this just above the class member you wish to ignore when doing a bulk add that covers it.
[String(Ignore = true)]
I have never used Web API before, but I need a web service that will accept/return JSON objects and using this seemed like a reasonable thing. It looked pretty simple (if not a bit of overkill for my purposes), but a data structure I need to deal with looks something like:
{
"values":["foo", "bar"],
"default":"bar"
}
And so I went to make a Model object:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
Problem is that default seems to be a protected keyword. There must be some way to get around that, right?
You can use keywords in C# as identifiers by prepending # in front of them.
I would suggest to go different way. Keep your C# object model as much standard as possible (I wouldn't use # sign and C# keywords as property name).
We can separate the serialized (JSON) world and C# objects - just by using the Json.NET features.
One of the simpliest to use is decoration with Attribute:
[JsonProperty(PropertyName = "default")]
public string DefaultValue { get; set; }
In this case we have to reference Newtonsoft.Json in the project. If it must be POCO, we can introduce CustomResolver derrived from DefaultContractResolver and define these conversions there...
But separation of concern in this case is a bit more pure solution, I would say
EDIT: JSON Contract Resolver draft (see comments)
Important NOTE: Newtonsoft.Json is part of the Web API. Not only it is an open source, but even MS team bet on that as a core JSON serializer.
1) Newtonsoft.Json (as a part of the Web.API) is already installed in your solution. So you do not have to downloaded (nuget) separately. It would always be in your packages folder. So, to use the attribute is just adding the reference. It is there...
2) There is a small draft how to do the attribute stuff, while keeping the POCO. As I've tried explain here: POCO's, behavior and Peristance Igorance, to keep POCO (e.g. we do profit from layered Architecture with NHibernate on a data layer), we can replace attributes with a Contract Resolver. Our POCO library does not have to reference anything
We just have to do extend the service layer:
public class MyResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
var jProperty = base.CreateProperty(member, memberSerialization);
var propertyInfo = member as PropertyInfo;
if (propertyInfo == null)
{
return jProperty;
}
// just adjust in case if Property name is DefaultValue
var isDefaultValueProeprty =
propertyInfo.Name.Equals("DefaultValue");
if(isDefaultValueProeprty)
{
jProperty.PropertyName = "default";
}
return jProperty;
}
...
This way we've provided the same information to serailizer as with the [JsonPropertyAttribute].
Now, we just have to use it. There are many ways (e.g. global) but we can do it for a controller only:
protected override void Initialize(HttpControllerContext context)
{
base.Initialize(context);
var jSettings = context.Configuration.Formatters.JsonFormatter.SerializerSettings;
jSettings.ContractResolver = MyResolver;
}
The class DropDownValues using camel convention:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
You can use prefix # to passby but it is still not following C# coding convention.
The better solution which you can both avoid reserved keyword and still use C# coding convention is using CamelCasePropertyNamesContractResolver:
class DropDownValues {
public string[] Values { get; set; }
public string Default { get; set; }
}
And customize JsonFormatter to avoid convention mismatch between C# and json object as below:
var jsonFormatter = configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
I have never used Web API before, but I need a web service that will accept/return JSON objects and using this seemed like a reasonable thing. It looked pretty simple (if not a bit of overkill for my purposes), but a data structure I need to deal with looks something like:
{
"values":["foo", "bar"],
"default":"bar"
}
And so I went to make a Model object:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
Problem is that default seems to be a protected keyword. There must be some way to get around that, right?
You can use keywords in C# as identifiers by prepending # in front of them.
I would suggest to go different way. Keep your C# object model as much standard as possible (I wouldn't use # sign and C# keywords as property name).
We can separate the serialized (JSON) world and C# objects - just by using the Json.NET features.
One of the simpliest to use is decoration with Attribute:
[JsonProperty(PropertyName = "default")]
public string DefaultValue { get; set; }
In this case we have to reference Newtonsoft.Json in the project. If it must be POCO, we can introduce CustomResolver derrived from DefaultContractResolver and define these conversions there...
But separation of concern in this case is a bit more pure solution, I would say
EDIT: JSON Contract Resolver draft (see comments)
Important NOTE: Newtonsoft.Json is part of the Web API. Not only it is an open source, but even MS team bet on that as a core JSON serializer.
1) Newtonsoft.Json (as a part of the Web.API) is already installed in your solution. So you do not have to downloaded (nuget) separately. It would always be in your packages folder. So, to use the attribute is just adding the reference. It is there...
2) There is a small draft how to do the attribute stuff, while keeping the POCO. As I've tried explain here: POCO's, behavior and Peristance Igorance, to keep POCO (e.g. we do profit from layered Architecture with NHibernate on a data layer), we can replace attributes with a Contract Resolver. Our POCO library does not have to reference anything
We just have to do extend the service layer:
public class MyResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
var jProperty = base.CreateProperty(member, memberSerialization);
var propertyInfo = member as PropertyInfo;
if (propertyInfo == null)
{
return jProperty;
}
// just adjust in case if Property name is DefaultValue
var isDefaultValueProeprty =
propertyInfo.Name.Equals("DefaultValue");
if(isDefaultValueProeprty)
{
jProperty.PropertyName = "default";
}
return jProperty;
}
...
This way we've provided the same information to serailizer as with the [JsonPropertyAttribute].
Now, we just have to use it. There are many ways (e.g. global) but we can do it for a controller only:
protected override void Initialize(HttpControllerContext context)
{
base.Initialize(context);
var jSettings = context.Configuration.Formatters.JsonFormatter.SerializerSettings;
jSettings.ContractResolver = MyResolver;
}
The class DropDownValues using camel convention:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
You can use prefix # to passby but it is still not following C# coding convention.
The better solution which you can both avoid reserved keyword and still use C# coding convention is using CamelCasePropertyNamesContractResolver:
class DropDownValues {
public string[] Values { get; set; }
public string Default { get; set; }
}
And customize JsonFormatter to avoid convention mismatch between C# and json object as below:
var jsonFormatter = configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};