Combining Attributes in C# - c#

I am using Attributes in a serializable properties object that is used to read/write xml config file for my app and display using Windows.Forms.PropertyGrid. I use c# Attributes to achieve this and would like to be able to combine values of attributes so that my [Description] includes [DefaultSettingValueAttribute].
For example, here is how one property is defined:
`[Category("General")]
[Description("Default Filename")]
[global::System.Configuration.DefaultSettingValueAttribute("sample.txt")]
public string DefaultFileName { get; set; } = "sample.txt";`
What I would like to be able to do is something on the lines of:
`[Category("General")]
[global::System.Configuration.DefaultSettingValueAttribute("sample.txt")]
[Description("Default Filename: " +
global::System.Configuration.DefaultSettingValueAttribute]
public string DefaultFileName { get; set; } = "sample.txt";`
Any suggestions on how I could achieve this?

At the very minimum, if two strings have different meaning then I wouldn't combine them. If you combine them then what you have is one big string, and whatever part of your code looks at that string has to know how to parse it back into its original pieces.
You can just as easily create an attribute that contains two strings. For example,
public class SomeAttribute : Attribute
{
public SomeAttribute() { }
public SomeAttribute(string category, string description)
{
Category = category;
Description = description;
}
public string Category { get; set; }
public string Description { get; set; }
}
No need to combine them.
But another question is, are the values related somehow so that they actually belong in the same attribute? I don't know how your attribute is used. But unless they're intrinsically related, it may be better to even keep them as separate attributes. If there's a logical distinction between two strings, use two strings, and the same for attributes.
Or perhaps what you might want is for one attribute to inherit from the other:
public class BaseAttribute : Attribute
{
public BaseAttribute() { }
public BaseAttribute(string defaultValue = null)
{
DefaultValue = defaultValue;
}
public string DefaultValue { get; set; }
}
public class SomeAttribute : BaseAttribute
{
public SomeAttribute() { }
public SomeAttribute(string category, string description, string defaultValue = null)
:base(defaultValue)
{
Category = category;
Description = description;
}
public string Category { get; set; }
public string Description { get; set; }
}
Now the second attribute contains its own properties plus that of the base attribute.

Related

XML attribute for ignoring class tag when serializating object c#

I need to get an xml output like this:
<Partners>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</Partner>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</Partner>
</Partners>
It has to be possible to add multiple PartnerID tags and multiple PartnerIDType tags in the parner tag. To get this result I was using a list of PartnerIdInfo. I had my classes like this:
public class Someclass
{
public List<Partner> Partners {get; set;}
}
public class Partner
{
public string PartnerType { get; set; }
[XmlElement("PartnerIdInfo")]
public List<PartnerIdInfo> PartnerIDInfos { get; set; }
}
public class PartnerIdInfo
{
public string PartnerID { get; set; }
public string PartnerIDType { get; set; }
}
The [XmlElement("PartnerIdInfo")] tag is for not showing the tag from the list property in the partner class. But it is still showing the tag :
<Partners>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerIdInfo>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</PartnerIdInfo>
<PartnerIdInfo>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</PartnerIdInfo>
</Partner>
</Partners>
Is there an attibute (or another solution) to do this? I prefer not to rewrite my code and fill everything in the code through the use of XmlDocument and XmlNode... Or will this be the only possible solution for this outcome?
Your problem is that you want the serializer to combine the value of Partner.PartnerType with the repeating values of Partner.PartnerIDInfos[*].PartnerID and Partner.PartnerIDInfos[*].PartnerIDType into a single omnibus <Partner> element. Unfortunately, this simply is not implemented. The content of each PartnerIdInfo will always be serialized within its own element.
Several workarounds can be found in the answers to the questions Keep sort when deserialize and serialize XML using XmlSerializer and Xml Deserialization - Merging two elements into a single List<T> object. In particular using a polymorphic array of simple elements with two possible types corresponding to <PartnerID> and <PartnerIDType> should meet your needs. You will need to modify Partner to mark the PartnerIDInfos as ignored and use a surrogate array for the elements:
public class Partner
{
[XmlElement(Order = 1)]
public string PartnerType { get; set; }
[XmlIgnore]
public List<PartnerIdInfo> PartnerIDInfos { get; set; }
[XmlElement(typeof(PartnerID), Order = 2), XmlElement(typeof(PartnerIDType), Order = 2)]
public StringElementBase [] XmlPartnerIDInfos
{
get => PartnerIDInfos?.SelectMany(p => new StringElementBase [] { new PartnerID { Value = p.PartnerID }, new PartnerIDType { Value = p.PartnerIDType } }).ToArray();
set => PartnerIDInfos = value?.OfType<PartnerID>().Zip(value.OfType<PartnerIDType>(), (id, type) => new PartnerIdInfo { PartnerID = id.Value, PartnerIDType = type.Value }).ToList();
}
}
public abstract class StringElementBase { [XmlText] public string Value { get; set; } } // From https://stackoverflow.com/a/48130816/3744182
public class PartnerID : StringElementBase { }
public class PartnerIDType : StringElementBase { }
Notes:
Marking StringElementBase.Value with [XmlText] causes the value to be serialized as text rather than nested markup.
StringElementBase [] XmlPartnerIDInfos should be an array rather than a List<StringElementBase> because XmlSerializer will not set back the value of a resizable collection such as List<T> after populating it.
Setting XmlElement.Order is optional but does indicate that <PartnerType> not come between any of the <PartnerID> and <PartnerIDType> elements.
In the setter for XmlPartnerIDInfos I don't attempt to validate that there are an equal number of PartnerID and PartnerIDType elements, or that the values alternate. You could add that, if you wish.
Demo fiddle here.

Binding C# BindingList<T> to a DataGridView

Maybe the question is a little bit dumb, but I did not quite find solution anywhere else.
So I am using a BindingList of custom made class objects as a DataSource for DataGridView.
Everything works fine with properties, that are directly inherited from other classes, but if I have an object of other class in the main class, its properties wont show up in DataGridView.
Classes are:
enum Valsts
{
Latvija,
Igaunija,
Ķīna,
ASV
}
class Razotajs
{
public Valsts valsts { get; set; }
public string razotajaNosaukums { get; set; }
}
class Tehnika
{
public string krasa { get; set; }
public Razotajs razotajs = new Razotajs();
}
class Viedierice : Tehnika
{
public string operetajsistema { get; set; }
public double ekranaIzmers { get; set; }
public bool irHDMI { get; set; }
}
class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim = new SimKarte();
public override string ToString()
{
return String.Join(";", modelis.ToString(),svars.ToString(),sim.veids.ToString(),operetajsistema.ToString(),ekranaIzmers.ToString(),irHDMI.ToString(),krasa.ToString(),razotajs.razotajaNosaukums.ToString(),
sim.numurs.ToString(),razotajs.valsts.ToString());
}
}
class SimKarte
{
public string veids { get; set;}
public int numurs { get; set; }
}
For example- I can see columns "modelis" and "svars", but attributes like "veids" and "numurs" from class SimKarte are not included in the DataGridView.
Is there any solution for this?
I've tried to add { get; set; } after declaring a new instance of an object in the class, but it's not even a real thing. I really don't have any idea, what would help me to solve this.
Thank you all in advance! :)
Honestly, I think the simplest solution is the one JohnG proposed; add proxy properties to your main class that read/write the properties of the complex objects
A datagridview will show only the simple types it knows how to show, from the top level class. It will not dig into properties of properties (otherwise even adding a string column would cause the grid to fill up with a Length column an Isinterned column etc..)
partial class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim { get; set; } = new SimKarte();
public override string ToString()
{
return String.Join(";",
modelis, svars, sim.veids, operetajsistema, ekranaIzmers, irHDMI, krasa, razotajs.razotajaNosaukums,
sim.numurs, razotajs.valsts);
}
}
partial class MobilaisTelefons {
public string SimVeids { get => sim.veids; set => sim.veids = value; }
public string SimNumers { get => sim.numers; set => sim.numers = value; }
public string RazotajsRazotajaNosaukums { get => razotajs.razotajaNosaukums; set => razotajs.razotajaNosaukums = value; }
public Valsts RazotajsValsts { get => razotajs.valsts; set => razotajs.valsts = value; }
}
Few tips:
I made the extension of the class partial so you can put it in another file. Hiding its members from intellisense would be hard work
the Enum column will probably show as an int. if you want it to be sensible, use a DataGridViewComboBox column bound to a list of all the enum values/names. On the column, set the DataMember to "RazotajsValsts", the DataSource to the list of enums, the DisplayMember to the property representing the enum name and the ValueMember to the property representing the enum value. See Enum.GetValues.
Enums should only have a plural name (if valsts is plural) if they are flags
classes should not have a plural name
public properties names should be in PascalCase not camelCase
I simplified your tostring: you don't need to call to string on everything; string join will do it. You especially don't need to call tostring on a string

newton json default string value not working?

When I seralise an object, for some string properties, I would like to output empty string other than ignore or output null.
According to newton's doc, I could do this:
public class Data
{
public int ProductId { get; set; }
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public string LargeData { get; set; }
}
However, in my test case, it still output null
Data D1 = new Data()
{
ProductId = 1
};
var b = JsonConvert.SerializeObject(D1);
The output is {"ProductId":1,"LargeData":null}. Am I doing something wrong?
Looking at DefaultValueHandling it doesn't look like there's any way of doing what you want.
The default value attribute is only used when deserializing, if the property isn't specified in the JSON. The ignore / include choices are the ones which are relevant when serializing, and those don't affect the value that's serialized - just whether or not it should be serialized.
Unless you've got code which actually sets the value to null, the simplest option would be to make the property default to "" from a .NET perspective:
public class Data
{
public int ProductId { get; set; }
public string LargeData { get; set; } = "";
}

Writing a concise string storage pattern

I have a series of classes used to represent identifiers in my project that are supposed to have a specific string storage format. I don't have control on this format.
The classes are pure containers, they don't do anything. The storage format is of the form "CLASSSTYPE|key1|key2|key3|...|keyN". Each "key" can be mapped to one property of the class.
Right now the FromStorageString and ToStorageString functions look like this:
public class SomeTypeId : IObjectId
{
public static string ToStorageString(SomeTypeId id)
{
return string.Format("{0}|{1}|{2}", typeof(SomeTypeId).Name, MyIntKey, MyStringKey);
}
public static SomeTypeId FromStorageString(IdReader source)
{
int intKey = source.Retrieve<int>();
string stringKey = source.Retrieve<string>();
return new SomeTypeId(intKey, stringKey);
}
public int MyIntKey { get; private set; }
public string MyStringKey { get; private set; }
public SomeTypeId(int intKey, string stringKey)
{
MyIntKey = intKey;
MyStringKey = stringKey;
}
}
We are checking the From/To consistency in unit tests, but I feel there should be a way to simplify the set up and perform the check a compile-time.
What I had in mind is something like this:
[Storage("MyIntKey", "MyStringKey")]
public class SomeTypeId : IObjectId
{
private SomeTypeId() {}
public int MyIntKey { get; private set; }
public string MyStringKey { get; private set; }
public SomeTypeId(int intKey, string stringKey)
{
MyIntKey = intKey;
MyStringKey = stringKey;
}
}
But first I don't know how to do this with the no parameters constructor and the property setters staying private. I am reluctant to have them public.
Second this approach is not robust to property name change and typos because the property names in the attribute are strings.
Should I expose the setters and private constructor ?
Is there a better way of doing this ?

Create inheritable LINQ-to-SQL mapable class

I am building a library for Windows Phone 8 which requires local databases. For obvious reasons, user of the library is going to create a mappable LINQ-to-SQL class with appropriate [Table]s and [Column]s. However, To in every such class, I need a few more columns for internal functioning of the library. The idea was that I would include a base class in the library which will have members corresponding to the required columns. The user would simply inherit from this class, add his own members and use that class as final LINQ-to-SQL map.
So far, my base class looks like this:
//Class to contain all the essential members
[Table]
public class SyncableEntityBase : NotifyBase, ISyncableBase
{
[Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)]
public int ItemId { get; set; }
[Column]
public bool IsDeleted { get; set; }
[Column]
public DateTime RemoteLastUpdated { get; set; }
[Column]
public DateTime LocalLastUpdated { get; set; }
}
And the derived class, something like this:
[Table]
public class ToDoCategory : SyncableEntityBase
{
private string _categoryName;
[Column]
public string CategoryName
{
get
{
return _categoryName;
}
set
{
if (_categoryName != value)
{
NotifyPropertyChanging("CategoryName");
_categoryName = value;
NotifyPropertyChanged("CategoryName");
}
}
}
private string _categoryColor;
[Column]
public string CategoryColor
{
get
{
return _categoryColor;
}
set
{
if (_categoryColor != value)
{
NotifyPropertyChanging("CategoryColor");
_categoryColor = value;
NotifyPropertyChanged("CategoryColor");
}
}
}
}
Idea is to have final class with the four essential columns and two added by user. According to MSDN documentation here, I need to append [InheritanceMapping] which requires the inherited type. However, as I am building a library, I have no way to know what types (and how many) the user will derive from my base class. Is there any way around this? How?

Categories