C# MongoDB set different Id representation for derived classes - c#

it's possible to change representation of inherited Id field from BsonType.ObjectId to BsonType.String?
public class BaseClass
{
public string Id { get; set; }
}
public class MyClass : BaseClass
{
}
I tried this approach, but it doesn't work.
BsonClassMap.RegisterClassMap<BaseClass>(cm =>
{
cm.AutoMap();
var id = cm.GetMemberMap(c => c.Id);
cm.SetIdMember(id);
cm.IdMemberMap.SetRepresentation(BsonType.ObjectId);
});
BsonClassMap.RegisterClassMap<MyClass>(cm =>
{
cm.AutoMap();
var idMember = cm.IdMemberMap; // idMember is null
idMember.SetRepresentation(BsonType.String);
});
Thanks a lot.

No, this is not possible. Derived classes cannot change the representation of fields in their base classes. Derived classes do of course have full control over how they want their fields to be serialized.

Related

Order property of derived class first in Swagger schema

I use, among others, Json.NET and NSwag and generate a swagger document. I have classes, Animal and Dog : Animal (inherits from Animal).
public class Animal
{
//properties
}
public class Dog : Animal
{
[JsonProperty(Order = -2)]
public string Name { get; set; }
}
I have property in Dog, which is Name. I want to sort Name before all of Dog's properties in the generated Swagger request schema.
Current result:
{
propertyOne: value1, //property of Animal
propertyTwo: value2, //property of Animal
Name: value3 //property of Dog
}
Desired result:
{
Name: value3, //property of Dog is sorted before everything else
propertyOne: value1,
propertyTwo: value2
}
I have done similar sorting but in Swashbuckle, might be similar functionality available in Nswag
Used SchemaFilter to sort properties.
services.AddSwaggerGen(swaggerGenOptions =>
{
.....
swaggerGenOptions.SchemaFilter<SwaggerSchemaFilter>();
}
In schemaFilter, I have used order logic to show all the basic datatype properties in their current oreder and properties which are custom classes in the end. But you can order by name or any other logic.
internal class SwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
schema.Properties = schema.Properties
.OrderBy(x => string.IsNullOrWhiteSpace(x.Value.Type) ? "z" : "a")
.ToDictionary(p2 => p2.Key, p2 => p2.Value);
}
}
For custom classes the type was null,
Also, you can get type from (SchemaFilterContext) context and use reflection to get more details.

How to map a list of objects to an object that contains multiple lists?

I am using automapper 9.0.0.
My situation is as follows. I have a list of items which are all instances of an abstract base class, let's call it BaseClass. There are 2 classes that inherit that class, let's call those Bar1Class and Bar2Class.
I want to map a list of BaseClass to an object that contains 2 lists. One list is a DTO for the Bar1Class objects from the list, and the 2nd one is for the Bar2Class objects from the list:
List<BaseClass> items = GetItems();
var dto = Mapper.Map<FooResponseModel>(items);
The hierarchy is as follows:
// Response models
public class FooResponseModel
{
public IEnumerable<Bar1Model> Bar1Models {get;set;}
public IEnumerable<Bar2Model> Bar2Models {get;set;}
}
public class Bar1Model
{
public string MyString {get;set;}
public int MyInt {get;set;}
}
public class Bar2Model
{
public string MyString {get;set;}
public bool MyBool {get;set;}
}
public abstract class BaseClass
{
public string MyString {get;set;}
}
public class Bar1Class : BaseClass
{
public int MyInt {get;set;}
}
public class Bar2Class : BaseClass
{
public bool MyBool {get;set;}
}
How would I set this up?
Using the CreateMap<BaseClass, FooResponseModel>() doesn't really work because I can't divide the collections. Doing something like CreateMap<Bar1Class, Bar1Model>() would allow me to map the classes itself, but not allow me to set up the lists.
Thanks!
Edit:
I would map it by hand like this now, because i dont know how to map the upper object correctly. I would of course add CreateMap<Bar1Class, Bar1Model>() and such beforehand.
var dto = new FooResponseModel
{
Bar1Models = items
.Where(x => x is Bar1Class)
.Cast<Bar1Class>()
.Select(x => Mapper.Map<Bar1Model>()),
Bar2Models = items.
.Where(x => x is Bar2Class)
.Cast<Bar2Class>()
.Select(x => Mapper.Map<Bar2Model>())
}
Create two maps between source classes and destination classes. Then, add mapping from a sequence of BaseClass to FooResponseModel and point AutoMapper how to populate Bar1Models and Bar2Models properties.
CreateMap<Bar1Class, Bar1Model>();
CreateMap<Bar2Class, Bar2Model>();
CreateMap<IEnumerable<BaseClass>, FooResponseModel>()
.ForMember(d => d.Bar1Models, o => o.MapFrom(s => s.Where(b => b is Bar1Class)))
.ForMember(d => d.Bar2Models, o => o.MapFrom(s => s.Where(b => b is Bar2Class)));

How to configure a mapping of collection types with AutoMapper?

For example, I've implemented two classes like these:
public class A
{
public List<C> Items { get; set; }
}
public class B
{
public IImmutableList<C> Items { get; set; }
}
public class C
{
}
When I try to map A to B and vice versa, I get an exception because List<string> cannot be converted to IImmutable<string>.
Probably I could provide a mapping for A<->B, but since it'll be a very common pattern in my solution, I'd like to avoid to manually mapping each class that may fall into the same case.
Is there anyway I can generalize the whole mapping using generic type definitions from a collection type to another collection type?
This is what I want to avoid
mapperConfig.CreateMap<A, B>()
.ForMember(a => a.Items, opts => opts.Ignore())
.AfterMap
(
(source, target) =>
{
target.Items = source.Items.ToImmutableList();
}
);

MongoDB .NET: Mapping inherited generic property

I have a generic class Parameter with a generic property Value:
abstract class Parameter<T> {
public T Value { get; set; }
}
StringParameter class inherits the Parameter class:
class StringParameter : Parameter<string> {
//...
}
Is it possible to properly map the StringParameter class so that it contains the generic Value property?
When trying to map the StringParameter class with the code below (and various other approaches) the best I could do is get an exception with the message: "The memberInfo argument must be for class StringParameter, but was for class Parameter`1."
BsonClassMap.RegisterClassMap<StringParameter>(cm => {
cm.AutoMap();
cm.MapMember(typeof(StringParameter).GetRuntimeProperty("Value"));
});
Mapping the Parameter class with the specified type parameter for every subclass that inherits it and then automapping each subclass seems to have done the trick.
BsonClassMap.RegisterClassMap<Parameter<string>>(cm => {
cm.AutoMap();
cm.MapProperty("Value");
});
BsonClassMap.RegisterClassMap<Parameter<DateTime>>(cm => {
cm.AutoMap();
cm.MapProperty("Value");
});
BsonClassMap.RegisterClassMap<Parameter<int>>(cm => {
cm.AutoMap();
cm.MapProperty("Value");
});
BsonClassMap.RegisterClassMap<Parameter<decimal>>(cm => {
cm.AutoMap();
cm.MapProperty("Value");
});
BsonClassMap.RegisterClassMap<StringParameter>();
BsonClassMap.RegisterClassMap<DateParameter>();
BsonClassMap.RegisterClassMap<IntegerParameter>();
BsonClassMap.RegisterClassMap<DecimalParameter>();
Note that this maps all the Parameter classes into a single collection with appropriate discriminators.

Polymorphism with AutoMapper

I have these business classes:
class BaseNode
{
public string name;
}
class CompositeNode : BaseNode
{
public List<BaseNode> childs = new List<BaseNode>();
}
And this flat dto:
class NodeDto
{
public string name;
public List<NodeDto> childs;
}
(note how all derived types are represented by one dto class)
I use auto mapper to do a conversion:
Mapper.CreateMap<BaseNode, NodeDto>()
.Include<CompositeNode, NodeDto>()
.ForMember(s => s.childs, prop => prop.Ignore());
Mapper.CreateMap<CompositeNode, NodeDto>();
Mapper.AssertConfigurationIsValid();
var root = new CompositeNode() { name = "root" };
var child = new CompositeNode {name = "child"};
var child2 = new CompositeNode { name = "child2" };
root.childs.Add(child);
child.childs.Add(child2);
var rootDto = Mapper.Map<CompositeNode, NodeDto>(root);
However the below is always null instead of having the child list:
rootDto.childs[0].childs
(i.e. only first level child is mapped correctly)
If I remove the prop.Ignore part I get an assert error that the childs property is not mapped.
What am I doing wrong?
This is old, but came across it looking for something else... You're telling it to ignore the childs field. AutoMapper is doing what it was told to do.
.ForMember(s => s.childs, prop => prop.Ignore());
You don't have properties in your classes public string Name {get;set;}, you have public Fields, I think that's the problem
also in order to map this classes you only need to create 2 simple maps
Mapper.CreateMap<CompositeNode, NodeDto>();
Mapper.CreateMap<BaseNode, NodeDto>()
.ForMember(s => s.childs, prop => prop.Ignore());;

Categories