I'm deserializing JSON into class objects and I would like to avoid duplicate ones. I've tried to implement IEqualityComparer, but it does not seem to work, because Equals() or GetHashCode() methods are never fired and resulting HashSet contains duplicates too. What's I'm doing wrong?
[DataContract]
public class Release : IEqualityComparer<Media>
{
public bool Equals(Media m1, Media m2)
{
if (m1.mediaFormat == m2.mediaFormat && m1.getTrackPosition() == m2.getTrackPosition()) { Console.WriteLine("Duplicate!"); return true; }
else return false;
}
public int GetHashCode(Media m) { Console.WriteLine("DDD!"); return m.ToString().GetHashCode(); }
[DataMember(Name = "title")]
public string releaseTitle { get; set; }
[DataMember(Name = "date")]
public string releaseDate { get; set; }
[DataMember(Name = "id")]
public string releaseMBID; //required for fetching artwork
[DataMember(Name = "media")]
public HashSet<Media> releaseMedia { get; set; }
public List<Media> getReleaseMedia() { return releaseMedia.ToList<Media>(); }
}
[DataContract]
public class Media
{
[DataMember(Name = "format")]
public string mediaFormat { get; set; }
[DataMember(Name = "track-offset")]
public string mediaTrackOffset { get; set; }
[DataMember(Name = "track-count")]
public string mediaTrackCount { get; set; }
public string getTrackPosition() { return (Convert.ToInt32(mediaTrackOffset) + 1).ToString() + "/" + mediaTrackCount; }
}
Yes you will need to pass the IEqualityComparer to the constructor like :
public class HFoo{
public string Name {get;set;}
public HashSet<Pet> Pets {get;set;} = new HashSet<Pet>(new CustomComparer());
}
public class Pet{
}
public class CustomComparer : IEqualityComparer<Pet>{
public int GetHashCode(Pet obj) {
return 1;
}
public bool Equals(Pet obj1, Pet obj2) {
return true;
}
}
Or the default object conparer will be used.
As you can see in the following demo: https://dotnetfiddle.net/Y9Afem.
The IEquatable will not be call.
Related
I'm sure someone has tried to do something like this before, but I'm unsure if what I'm finding in my searches fits what I'm trying to do.
In my .Net 6 Web API I have a class to get data passed by the request:
public abstract class QueryStringParameters {
private readonly int _maxPageSize = Constants.DefaultPageSizeMax;
private int _pageSize = Constants.DefaultPageSize;
public int? PageNumber { get; set; } = 1;
public int? PageSize {
get => _pageSize;
set => _pageSize = value > _maxPageSize ? _maxPageSize : value ?? Constants.DefaultPageSize;
}
public string OrderBy { get; set; }
public string Fields { get; set; }
}
For each controller I create a view model which inherits from this:
public class ProgramParameters : QueryStringParameters {
public bool MapDepartment { get; set; } = true;
public bool MapAnother1 { get; set; } = true;
public bool MapAnother2 { get; set; } = true;
...
public ProgramParameters() {
// Default OrderBy
OrderBy = "Id";
}
}
This works fine when calling an endpoint expecting multiple results and single results. However, I want to split the QueryStringParameters properties that are for pagination, something like this:
public abstract class QueryStringParameters {
public string Fields { get; set; }
}
public abstract class QueryStringParametersPaginated : QueryStringParameters {
private readonly int _maxPageSize = Constants.DefaultPageSizeMax;
private int _pageSize = Constants.DefaultPageSize;
public int? PageNumber { get; set; } = 1;
public int? PageSize {
get => _pageSize;
set => _pageSize = value > _maxPageSize ? _maxPageSize : value ?? Constants.DefaultPageSize;
}
public string OrderBy { get; set; }
}
The problem is that then my view modal looks like this:
public class ProgramParameters : QueryStringParameters {
public bool MapDepartment { get; set; } = true;
public bool MapAnother1 { get; set; } = true;
public bool MapAnother2 { get; set; } = true;
...
public ProgramParameters() {
}
}
public class ProgramParametersPaginated : QueryStringParametersPaginated {
public bool MapDepartment { get; set; } = true; // repeated
public bool MapAnother1 { get; set; } = true; // repeated
public bool MapAnother2 { get; set; } = true; // repeated
...
public ProgramParameters() {
// Default OrderBy
OrderBy = "Id";
}
}
How can I rewrite this so that ProgramParameters and ProgramParametersPaginated don't have to have the same properties (MapDepartment, MapAnother1, MapAnother2) defined in both?
I tried something like this but that's not allowed and I am unsure how to proceed.
public class ProgramParametersPaginated : ProgramParameters, QueryStringParametersPaginated {
public ProgramParametersPaginated() {
// Default OrderBy
OrderBy = "Id";
}
}
If I understood correctly, you need to extract interfaces instead of using classes as you did, so you can apply multiple implementation.
First define the interfaces and constants for you filter properties:
public enum Constants
{
DefaultPageSizeMax = 500,
DefaultPageSize = 100
}
public interface IQueryStringParameters
{
string Fields { get; set; }
}
public interface IQueryStringParametersPaginated : IQueryStringParameters
{
string OrderBy { get; set; }
int PageSize { get; set; }
int MaxPageSize { get; set; }
int? PageNumber { get; set; }
}
Then you create an abstract class that inherit from both interfaces defined so you can write some behaviour like you did with the setters and getters:
public abstract class BaseProgramParameters : IQueryStringParameters, IQueryStringParametersPaginated
{
public string Fields { get; set; }
public string OrderBy { get; set; }
private int _pageSize = (int)Constants.DefaultPageSize;
private int _maxPageSize = (int)Constants.DefaultPageSizeMax;
public int PageSize
{
get => _pageSize;
set => _pageSize = value > _maxPageSize ? _maxPageSize : value;
}
public int MaxPageSize { get; set; }
public int? PageNumber { get; set; }
public bool MapDepartment { get; set; } = true;
public bool MapAnother1 { get; set; } = true;
public bool MapAnother2 { get; set; } = true;
public BaseProgramParameters()
{
}
public BaseProgramParameters(string orderBy)
{
this.OrderBy = orderBy;
}
}
Since you may want to define a different value on MapDeparment, MapAnother, etc, you can use the constructor on the child classes:
public class ProgramParametersPaginated : BaseProgramParameters
{
public ProgramParametersPaginated() : base("Id")
{
}
}
public class ProgramParameters : BaseProgramParameters
{
public ProgramParameters()
{
this.MapAnother1 = false;
}
}
Let me know if you have any further doubts.
I have a flat complex type which I need to map to a complex type within a list. I have achieved it using the below code but it is not elegant. The mapping for each individual item has to be specified explicitly even though the types and names match. I wanted to know if there is a more elegant way of doing this without such verbosity and tight coupling?
using AutoMapper;
MapperConfiguration _config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<FlatObject, MyDTO>()
.ForMember(dst => dst.ListObject, opt => opt.MapFrom(src => new List<ListObject> {
new ListObject {
DTOCustObject = new DTOCustObject {
MyString = src.CustType.MyString,
MyInt = src.CustType.MyInt,
MyBool = src.CustType.MyBool,
//Others...
DTOMyObject = new DTOMyObject {
SomeString = src.CustType.MyObject.SomeString
//Others...
}
}
}
}));
});
_config.AssertConfigurationIsValid();
var flatObject = new FlatObject();
flatObject.CustType.MyString = "ABC123";
flatObject.CustType.MyInt = 12345;
flatObject.CustType.MyBool = true;
flatObject.CustType.MyObject.SomeString = "Some String Content";
IMapper mapper = new Mapper(_config);
var myDTO = mapper.Map<MyDTO>(flatObject);
Console.ReadKey();
//###############
//Entity - Source
//###############
public class FlatObject
{
public CustType CustType { get; set; } = new CustType();
}
public class CustType
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
}
public class MyObject
{
public string? SomeString { get; set; }
}
//#################
//DTO - Destination
//#################
public class MyDTO
{
public List<ListObject> ListObject { get; set; } = new List<ListObject>();
}
public class ListObject
{
public DTOCustObject DTOCustObject { get; set; } = new DTOCustObject();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
}
public class DTOMyObject
{
public string? SomeString { get; set; }
}
public class MyDTO
{
public List<DTOCustObject> DTOCustObjectList{ get; set; } = new List<DTOCustObject>();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
public DTOCustObject(ICustObjectMapper mapper){
MyString = mapper.GetMyString();
MyInt = mapper.GetMyInt();
MyBool = mapper.GetMyBool();
DTOMyObject = mapper.GetDTOMyObject();
}
}
public class DTOMyObject
{
public string? SomeString { get; set; }
public DTOMyObject(IDTOmyObjectMapper mapper)
{
SomeString = mapper.GetSomeString();
}
}
Now simply implement the Interfaces and their GetMethods on the source object, in the corresponding classes. And presto. No need for any framework of any kind.
This means, no framework, will try change syntax, and force a rewrite. You aren't locked into keeping it updated, there won't be any dependencies on it, etc.
You are free, and the time it takes you to configure, and annotate the code, is equal to the amount of time it takes you to just write the damn interfaces and implementations.
If your objects are autogenerated, simply use the partial keyword, and make the interface implementations in a partial class. Problem solved.
Something to this effect:
public class CustType : ICustObjectMapper
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
string GetMyString(){
return MyString;
}
// etc for the rest of the basic types.
DTOMyObject GetMyObject(){
return new DTOMyObject(MyObject)
}
}
public class MyObject : IDTOmyObjectMapper
{
public string? SomeString { get; set; }
public string GetSomeString()
{
return SomeString;
}
}
Of course, if you don't know how to declare an interface...
public interface ICustObjectMapper {
string GetMyString();
//etc.
}
Now please, for the love of everything on this planet. STOP USING MAPPING FRAMEWORKS IN C#, IT MAKES NO SENSE!
I want to create a method that will create a List of generic objects. Is it possible? Something like this:
public class Mtrl
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
public string Aa { get; set; }
}
And then with a generic class to create my list:
public class GenericClass<T>
{
public List<T> GetData<T>()
{
List<T> myList = new List<T>();
if (typeof(T) == typeof(Trdr))
{
myList.Add(new Trdr());//Error 1: cannot convert from Trdr to 'T'
}
if (typeof(T) == typeof(Mtrl))//Error 2: cannot convert from Mtrl to 'T'
{
myList.Add(new Mtrl());
}
return myList;
}
}
My mistake. I will try to clarify more. The Classes Trdr,Mtrl etc will have many different properties. The Getdata method will take data from a web service via json and i want to create a List of Objects and return it
Something like this:
public List<T> GetData<T>()
{
List<T> myList = new List<T>();
if (typeof(T) == typeof(Trdr))
{
for (int i = 0; i < 100; i++)//fetch data from web api in json format
{
Trdr NewObj = new Trdr();
NewObj.Aa = "...";
NewObj.AFM = "...";
myList.Add(NewObj);
}
}
if (typeof(T) == typeof(Mtrl))
{
for (int i = 0; i < 100; i++)
{
Mtrl NewObj = new Mtrl();
NewObj.Aa = "...";
NewObj.Name = "name ...";
myList.Add(NewObj);
}
}
return myList;
}
}
I suggest extracting an interface (or even a base class):
//TODO: Since you have more than 2 classes, please check the common interface
public interface Idr {
string Id { get; set; }
string Name { get; set; }
string Code { get; set; }
string Aa { get; set; }
}
With all classes of interest implementing it:
public class Mtrl : Idr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr : Idr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
}
Now you can use List<Idr> collection; we want the class (T) that implements Idr to have a parameterless constructor as well (new()):
public class GenericClass<T> where T : Idr, new()
{
public List<T> GetData()
{
List<T> myList = new List<T>() {
new T();
};
return myList;
}
}
Sounds like you are trying to create a new list that contains some initial data, and you need to do this for many different types T.
If Mtrl and Trdr has nothing in common and are handled completely differently, consider simply using different methods to get each type of list:
GetDataMtrl(){
var result = new List<Mtrl>();
result.Add(new Mtrl());
return result;
} // etc
Otherwise what you need is Type Constraints of Generic Parameters, that can tell the compiler more about what T is. Or rather what the different values of T must have in common. For example you can do
public List<T> GetData<T>() where T : new()
{
List<T> myList = new List<T>();
myList.Add(new T());
return myList;
}
To say that for T it has to be possible to do new T(). Then you can say GetData<Trdr>() to get a list that contains a single empty Trdr, as you code might seem to try to do.
Maybe you need to set some default values in the data. I notice the classes has a lot of variables in common. Then you might consider using inheritance to specify this commonality:
public class Mtrl
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr : Mtrl
{
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
}
And then write generic code where T is either of the type Mtrl or its descendant class:
public List<T> GetData<T>() where T : Mtrl
{
List<T> myList = new List<T>();
T MtrlOrTrdr = new T();
MtrlOrTrdr.Id = "my-new-id-";
myList.Add(MtrlOrTrdr);
return myList;
}
I want to be able to save an arbitrary flat object into the name-value list.
public class NameValueListEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[InverseProperty(nameof(NameValueListContentEntity.Entity))]
public ICollection<NameValueListContentEntity> Content { get; set; }
}
public class NameValueListContent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("entity_fk")]
public NameValueListEntity Entity { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class ObjectToSave
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
I could use reflection to manually assemble/parse the list, but it will create a lot of overhead. Lots of NameValueListContent objects will be needlessly created both during the saving and the reading. Could it somehow be omitted? Especially during the reading, which is very performance-sensitive in my case.
Assume you have a AppDbContext class that holds your NameValueListContent class objects named as NVListContents. You can read and write the name-value list of objects by doing the following:
public class AppDbContext : DbContext
{
public DbSet<NameValueListContent> NVListContents { get; set; }
public AppDbContext()
: base()
{ }
}
public class SomeClass
{
private AppDbContext context { get; set; }
public SomeClass(AppDbContext _context)
{
context = _context;
}
public List<ObjectToSave> ReadObjects()
{
return context.NVListContents
.Select(nvlc => new ObjectToSave { Prop1 = nvlc.Name, Prop2 = nvlc.Value
}).ToList();
}
public bool WriteObjects(int id, string name, string value)
{
var query = context.NVListContents
.FirstOrDefault(nvlc => nvlc.Id == id);
if(query != null)
{
query.Name = name;
query.Value = value;
context.Update(query);
context.SaveChanges();
return true;
}
else
{
return false;
}
}
}
Hope, this answers to your question.
I have below classes with which I generate a request object and serialize the response to get a JSON string. The definition of classes are as follows
PortfolioResponse & StocksInfo classes are for mentioning purpose as they contain a lot many fields which I would not be able to write here.
[DataContract]
public class BaseInfoRequest
{
[DataMember]
public string Token { get; set; }
}
[DataContract]
public class StocksInfoRequest : BaseInfoRequest
{
[DataMember]
public string SomeKey { get; set; }
}
[DataContract]
public class PortfolioInfoRequest : BaseInfoRequest
{
[DataMember]
public string PortfolioId { get; set; }
[DataMember]
public string SomeKey { get; set; }
}
[DataContract]
public class StocksInfoResponse
{
[DataMember(Name = "success")]
public bool Success { get; set; }
[DataMember(Name = "message")]
public string Message { get; set; }
[DataMember(Name = "stocks")]
public StocksInfo StocksInfo { get; set; }
[DataMember(Name = "pfolio")]
public PortfolioResponse PortfolioInfo { get; set; }
}
public class PortfolioResponse
{
}
public class StocksInfo
{
}
Now, to get the data from different other classes and services in project I use below two methods in let's say class Sample
private string GetsStocksInformation(StocksInfoRequest request)
{
var response = new StocksInfoResponse();
if (ValidateRequestToken(request) || string.IsNullOrWhiteSpace(request.SomeKey))
{
response.Message =
GetsResponse().ErrorMessage;
return JsonConvert.SerializeObject(response);
}
response.StocksInfo = GetsStocksInfo(request);
response.Success = true;
return JsonConvert.SerializeObject(response);
}
private string GetsPortfolioInformation(PortfolioInfoRequest request)
{
var response = new StocksInfoResponse();
if (!ValidateRequestToken(request) || string.IsNullOrWhiteSpace(request.SomeKey) ||
string.IsNullOrWhiteSpace(request.PortfolioId))
{
response.Message =
GetsResponse().ErrorMessage;
return JsonConvert.SerializeObject(response);
}
response.PortfolioInfo = GetsPortfolioInfo(request.SomeKey, request.PortfolioId);
response.Success = true;
return JsonConvert.SerializeObject(response);
}
Now, if you look closely enough both the methods are almost same except condition
if (ValidateRequestToken(request) || string.IsNullOrWhiteSpace(request.SomeKey)) &&
response.StocksInfo = GetsStocksInfo(request);
Apart from above two, both the methods do more or less same things. The definition of ValidateRequestToken is below
private bool ValidateRequestToken(BaseInfoRequest request)
{
return true;
}
How do I combine both methods to create a generic method ?