How can I show the classes that inherit from my base class in the scheme?
I tried a couple of things. (I use System.Text.Json.Serialization;)
public class Class
{
public string A { get; set; }
public string B { get; set; }
}
public class AClass : Class
{
public string C { get; set; }
}
public class BClass : Class
{
public string D { get; set; }
}
If I use [ProducesResponseType(typeof(Class), StatusCodes.Status200OK)] it only shows :
{
"a": "string",
"b": "string
}
but not c and d.
Any idea? I use .NET Core 7.
Related
i have a base class which has 2 derived classes and would like to map one field of the output to a "oneof" statement.
public class ClothingCollection
{
public string Name { get; set; }
public string Id { get; set; }
}
public abstract class ClothingCollection<T> : ClothingCollection
{
public abstract string Type { get; }
public abstract List<T> Stuff { get; }
}
public class PantsCollection : ClothingCollection<PantsModel>
{
public override string Type { get; } = "PANTS";
public override List<PantsModel> Stuff { get; }
}
public class ShirtCollection : BaseItem<ShirtModel>
{
public override string Type { get; } = "SHIRT";
public List<BarChartData> Data { get; }
}
public class PantsModel
{
public bool IsJeans { get; set; }
public string owner { get; set; }
}
public class ShirtModel
{
public string Brand { get; set; }
public int Quantity { get; set; }
}
and set the controller response as such ...
[ProducesResponseType(typeof(List<ClothingCollection>), 200)]
[HttpGet("HelloWorld")]
public IActionResult HelloWorld()
{
....
}
and now would like the resulting swagger documentation to be List that looks somewhat like the following (if it's even possible)
{
"Name": "string",
"Id": "string",
"type": "string",
"Stuff":[
anyof -> PantsCollection,
ShirtCollection
]
}
The concept where you have exactly one value of one of several fixed types is called a tagged union, discriminated union, or sometimes a either monad.
You could create a class that has either shirts or pants:
public class ShirtOrPants
{
public ShirtCollection Shirts { get; }
public PantsCollection Pants { get; }
public ShirtOrPants(ShirtCollection shirts, PantsCollection pants)
{
Shirts = shirts;
Pants = pants;
if ((shirts == null) == (pants == null))
{
throw new InvalidOperationException("Must have shirts OR pants!");
}
}
}
There are as far as I know no built in support for tagged unions in c# or any of the more popular serialization languages, so you would need some custom solution. You would typically add various methods to process either shirts or pants depending on which one has a value.
There are generic implementations available, but they might not be very helpful if the goal is to provide readable API definitions.
I have some abstract base classes to be used on multiple implementations.
Base classes:
public abstract class BaseX
{
public string A { get; set; }
}
public abstract class BaseY : BaseX
{
public string B { get; set; }
}
For each use case, I want to create from these base classes specific classes like:
public abstract SpecificX : BaseX
{
public string C { get; set; }
}
public abstract SpecificY : BaseY
{
public string D { get; set; }
}
All classes that derive from SpecificY should contain all the properties A, B, C, D.
My problem now is, that SpecificY doesn't have the property C from SpecificX, because I cannot do multiple inheritance like
public abstract SpecificY : BaseY, SpecificX
My only idea would be to use Interface like this:
public Interface ISpecificX
{
string C { get; set; }
}
public abstract SpecificX : BaseX, ISpecificX
{
public string C { get; set; }
}
public abstract SpecificY : BaseY, ISpecificY
{
public string D { get; set; }
public string C { get; set; } <== redundancy
}
But then I'd need to implement C twice. And as soon as C is becoming more than a simple Property, things get ugly. Is there a better way to create this structure?
Thanks in advance,
Frank
I would strongly suggest to favour composition over inhertiance - as propsed by the GoF. This way you do not inherit a given class, but just use an instance of it. Then you can easily have all your properties without any duplication:
class BaseX { ... }
class BaseY { ... }
class SpecificY : BaseY
{
private readonly SpecificX b = new SpecificX();
public string A { get => this.b.A; set => this.b.A = value; } // delegate the call
public string B { get; set; }
public string C { get => this.b.C; set => this.b.C = value; } // delegate the call
public string D { get; set; }
}
Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.
I have json:
[
{
"Name" : "SHA of timestamp",
"ProjectURL" : "URL to Proj",
"AccountIdentifier" : "Account Identifier",
"Repositories":
{
"GitLink1":
{
"Login" : "login1",
"Password" : "pas1"
},
"GitLink2":
{
"Login" : "login2",
"Password" : "pas2"
},
...
"GitLink999":
{
"Login" : "login999",
"Password" : "pass999"
}
}
},
...
{
Same entry
}
]
I need to fill it in IEnumerable of my created classes
public class Git
{
public string Login { get; set; }
public string Password { get; set; }
}
public class Repositories
{
public Git git { get; set; }
}
public class ConfigEntry
{
public string Name { get; set; }
public string ProjectURL { get; set; }
public string AccountIdentifier { get; set; }
public Repositories Repositories { get; set; }
}
Using
IEnumerable<ConfigurationEntry> config = JsonConvert.DeserializeObject<IEnumerable<ConfigurationEntry>>(CONFIGJSON);
Where CONFIGJSON contains frst json file.
Gives me this data of ConfigEntry class :
Name : "filled with some data"
ProjectURL : same
AccountIdentifier : same
Repositories : NULL
How i can fill Repositories field with all data i have in config JSON?
Is there any ideas.
It looks like you probably shouldn't have a Repositories class - instead, change your ConfigEntry.Repositories property to:
public Dictionary<string, Git> Repositories { get; set; }
Then you'll get a dictionary where the key of "GetLink1" has a value with Login of "login1" etc.
Your class definitions aren't quite right.
Your "repositories" class only contains a single Git, but needs to be a Dictionary.
I think if you use this you will be close:
public class Git
{
public string Login { get; set; }
public string Password { get; set; }
}
public class ConfigEntry
{
public string Name { get; set; }
public string ProjectURL { get; set; }
public string AccountIdentifier { get; set; }
public Dictionary<string,Git> Repositories { get; set; }
}
you may also need a non-generic IEnumerable, so it knows which objects to actually create:
JsonConvert.DeserializeObject<List<ConfigurationEntry>>(CONFIGJSON);
I have a problem while adding a new class in my existing structure. I am going to explain my problem as much clear as i can
public interface Imust
{
string Name { get; set; }
string File { get; set; }
string RowKey { get; set; }
string Time { get; set; }
string PartitionKey { get; set; }
}
public class TA : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class TB : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class TC : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class _Table <T> : _Account where T : Imust
{
}
Here the above 3 classes are implemented as Tables and its properties as its columns in my project. Imust interface is implemented in each class because in generic class i put an interface constraint. TableServiceEntity class contains the implementation for RowKey and PartitionKey.And this class is also inherited in all 3 entities.
Problem : Now i have to add a new table in my application. So for this i have to add a new class here which is
public class TD : TableServiceEntity
{
}
I do not want this class to implement the Imust interface because it does not contain these columns. But i have to pass it as a parameter in generic class _Table.Because this new class has different columns but it perform same function which other 3 entities does. Now how will i add this new class while maintaining my existing structur ?
Please suggest me any better solution for this problem ?
EDIT
Yes i can put a constraint TableServiceEntity as a base class. But in generic class _Table there are few function which operate on File property like
public T AccessEntity(string Id = "0", string File = "0")
{
return (from e in ServiceContext.CreateQuery<T>(TableName)
where e.RowKey == Id || e.File == File
select e).FirstOrDefault();
}
If i removed this interface constraint then it shows an error that T does not have a defination for File.
I'd do this... the interface has no sense in your declaration as the more generic type for table is TableServiceEntity
public class _Table <T> : _Account where T : TableServiceEntity
{
}
Split the interface in two:
public interface Ibase
string RowKey { get; set; }
string PartitionKey { get; set; }
}
public interface Imust : Ibase
{
string Name { get; set; }
string File { get; set; }
string Time { get; set; }
}
public class TA : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class TB : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class TC : TableServiceEntity, Imust
{
public string Time { get; set; }
public string Name { get; set; }
public string File { get; set; }
}
public class _BaseTable <T> : _Account where T : Ibase
{
}
public class _Table <T> : _BaseTable<T> where T : Imust
{
}
And implement common functionality in _BaseTable and this specific to Time, Name and File in _Table.
It's even more intuitive after and edit you have made to your question. Thos methods in _BaseTable that rely on File, Name or Time can be marked abstract and overriden in _Table.
Updated to reflect posters additional info:
You need to specify a new Interface:
public interface IMust2 {
public string File {get;set;}
public string Rowkey {get;set;
}
Modify IMust to inherit from IMust2
public interface IMust : IMust2
{
public string Name {get;set;}
public string Time {get;set;}
public string PartitoinKey {get;set;}
}
Why not just have _Table be of <TableServiceEntity> type? Obviously, you are breaking your interface, so you can't keep using it as the generic as not every class will be of that interface?
You have 3 classes (TA, TB and TC) that are exactly the same. Why don't you have a single class, though?
For behavior (that is, methods), use a interface. For structure (that is, properties), use inheritance (like at TableServiceEntity).
Make TD inherit from the base class but do not implement the interface.
Change the restriction at _Table to be where T : TableServiceEntity
Regards