Automatically determine MaxLength and use it for padding - c#

I have a model with a bunch of fields defined like this:
public class Transaction
{
public DateTime R03DateFrom { get; set; }
public DateTime? R03DateTo { get; set; }
[MaxLength(80)]
public string R03Place { get; set; }
[MaxLength(20)]
public string R03Code { get; set; }
// And so on....
}
At a certain point I need to export some of this data to fixed width files, and if a string has a MaxLength of x, then in the output file it should always be output right-padded with spaces up to x characters.
I'm hoping to re-use the fact that the string's MaxLength is always defined in the Model in order to flow this information through to the export.
At the moment, a typical export row function looks like this (ExDateTime is an extension method that formats the date in yyyyMMddhhmm format):
private string GetR03Row()
{
return GetRowCode() + "03" +
R03DateFrom.ExDateTime() +
R03DateTo.ExDateTime() +
(R03Place??"").PadRight(80) +
(R03Code??"").PadRight(20);
}
I'd like to replace the line
(R03Place??"").PadRight(80)
with something that uses the MaxLength attribute.
Every string will have a MaxLength.
UPDATE:
I've taken the suggestion below and turned it into an Extension Method:
public static string PadToMax<T>(this string source, string propName)
{
PropertyInfo[] props = typeof(T).GetProperties();
var found = props.Where(m => m.Name.Equals(propName));
if (!found.Any()) return source;
var propertyInfo = found.First();
var attrs = propertyInfo.GetCustomAttributes(false);
if (!attrs.Any()) return source;
foreach (var maxLengthAttribute in attrs.OfType<MaxLengthAttribute>())
{
return (source??"").PadRight(maxLengthAttribute.Length);
}
return source;
}
which allows me to use this syntax to achieve what I want:
// (R03Place??"").PadRight(80) turns into
R03Place.PadToMax<Transaction>(nameof(R03Place))
This is fine. I'd love it if I could change the extension method to somehow work out the "Transaction" type and the name of the source string variable. But thi is close enough.

What Olivier proposed should work.
Here another way:
private static void Main(string[] args)
{
var maxLength = GetMaxLengthAttributeValue<Transaction>("R03Place");
Console.WriteLine("R03Place = {0}",maxLength);
maxLength = GetMaxLengthAttributeValue<Transaction>("R03Code");
Console.WriteLine("R03Place = {0}",maxLength);
Console.ReadLine();
}
public static int? GetMaxLengthAttributeValue<T>(string propertyName)
{
PropertyInfo[] props = typeof (T).GetProperties();
var found = props.Where(m => m.Name.Equals(propertyName));
if (!found.Any()) return null;
var propertyInfo = found.First();
var attrs = propertyInfo.GetCustomAttributes(false);
if (!attrs.Any()) return null;
foreach (object attr in attrs)
{
MaxLengthAttribute maxLengthAttribute = attr as MaxLengthAttribute;
if (maxLengthAttribute != null)
{
return maxLengthAttribute.Length;
}
}
return null;
}
Put the method in a helper class:
//You can use it as:
(R03Place??"").PadRight(YourHelper.GetMaxLengthAttributeValue<Transaction>("R03Place").Value);
// with C# 6, you don't have to hard code the property name
(R03Place??"").PadRight(YourHelper.GetMaxLengthAttributeValue<Transaction>(nameof(R03Place)).Value);

Could you save the maxlength as a private field?
public class Transaction
{
private int maxLengthPlace = 80
public DateTime R03DateFrom { get; set; }
public DateTime? R03DateTo { get; set; }
[MaxLength(maxLengthPlace)]
public string R03Place { get; set; }
[MaxLength(20)]
public string R03Code { get; set; }
// And so on....
}
and then use the same field later on?
(R03Place??"").PadRight(maxLengthPlace)

Related

Simplify passing nameof(someObject) and someObject as parameters to a method

I have a method where I'm currently passing in both someObject and nameof(someObject) since I'm using this method many times I'm wanting to simplify my code by finding a way to only pass in the object once but if I do the nameof() inside the method I'm obviously not going to get the name I want.
What I have currently is something like this:
public record ResourceObject
{
public string Name { get; init; }
public byte[] File { get; init; }
public string Content { get; init; }
public ResourceObject(string name, byte[] file)
{
Name = name;
File = file;
Content = Encoding.Default.GetString(file);
}
}
Where the use looks like this:
var test = new ResourceObject(nameof(Properties.Resources.SomeResource), Properties.Resources.SomeResource)
Ideally, I'd like to get the use to look like this(I don't think this is possible):
var test = new ResourceObject(Properties.Resources.SomeResource)
I did find a post that shows getting the name like this but then I can't get the object itself:
public record ResourceObject
{
public string Name { get; init; }
public byte[] File { get; init; }
public string Content { get; init; }
private ResourceObject(string name, byte[] file)
{
Name = name;
File = file;
Content = Encoding.Default.GetString(file);
}
public static ResourceObject New<T>(Expression<Func<T>> property)
{
var name = (property.Body as MemberExpression).Member.Name;
var file = ???; //I can't figure out how to get the object itself here
return new(name, file);
}
}
The use for that looks like this (which would be great improvement over what I if I could get it to work):
var test = ResourceObject.New(() => Resources.TradeEngineSettings_Base);
This is what ultimately worked for me.
public record ResourceObject<T>
{
public string Name { get; }
public T Object { get; }
public string Content { get; }
public ResourceObject(T #object, [CallerArgumentExpression(nameof(#object))] string name = null)
{
Name = name.Split('.', StringSplitOptions.RemoveEmptyEntries)[^1];
Object = #object;
if (#object?.GetType() == typeof(byte[]))
{
Content = Encoding.Default.GetString(#object as byte[] ?? Array.Empty<byte>());
}
}
}
To keep Name more similar to a nameof() behavior, name is split on '.' and only takes the last item in the array.

How to convert BigQuery data to custom type?

Does anybody know how to convert Google.Cloud.BigQuery.V2.BigQueryResults to a custom type? As far as I see the BigQueryClient type doesn't have generic versions of ExecuteQuery and ExecuteQueryAsync.
According to Google BigQuery quickstart guide we can do something like that:
public class Foo
{
public string FirstValue { get; set; }
public string SecondValue { get; set; }
public string ThirdValue { get; set; }
}
public IEnumerable<Foo> GetQueryResult()
{
var queryExecutionResult = client.ExecuteQuery(Query, null);
foreach (var row in queryExecutionResult)
{
yield return new Foo
{
FirstValue = row["column1"],
SecondValue = row["column2"],
ThirdValue = row["column3"]
};
}
}
I have implemented my custom converter which uses reflection under the hood. I don't quite like a reflection. So perhaps somebody knows some better way. I put my implementation below.
public class Foo
{
// I use custom attribute which is similar to DescriptionAttribute.
[BigQueryColumnName("column1")]
public string FirstValue { get; set; }
[BigQueryColumnName("column2")]
public string SecondValue { get; set; }
[BigQueryColumnName("column3")]
public string ThirdValue { get; set; }
}
public IEnumerable<Foo> GetQueryResult()
{
var queryExecutionResult = client.ExecuteQuery(Query, null);
return queryExecutionResult.Select(bigQueryRowToFooConverter.ConvertToFoo);
}
public Foo ConvertToFoo(BigQueryRow row)
{
var item = new Foo();
foreach (var propertyInfo in typeof(Foo).GetProperties())
{
var attribute = (BigQueryColumnNameAttribute)Attribute.GetCustomAttribute(propertyInfo, typeof(BigQueryColumnNameAttribute));
var value = row[attribute.ColumnName];
propertyInfo.SetValue(item, value);
}
return item;
}

How to trim all strings from a complex object returned with dapper

I am working with a legacy database, within this database, the data get assigned the maximum length of the column. if the string data is shorter, it will automaticly fill in whitespaces at the end.
What i'm trying to do is trim all these ending whitespaces with every query i do.
i figured one of the better ways would be making an extension method for dapper query using reflection.
But i can't seem to get it to work.
parent entity:
public class Person: BaseEntity
{
public Identification Identification { get; set; }
public Address Address { get; set; }
public Contact Contact { get; set; }
public Family Family { get; set; }
public ICollection<Payment> Payments { get; set; }
}
example of child entity:
public class Address: BaseEntity
{
public string Street { get; set; }
public int Number { get; set; }
public string BoxNumber { get; set; }
public int ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
Now i do my join query like this:
var result = _db.QueryTrim<dynamic>(sql, new { userid = id })
.Select(p => new Person()
{
Identification = IdentificationMapper.MapToIdentificationEntity(p),
Address = AddressMapper.MapToAddressEntity(p)}).First();
i am trying to implement something like this but i can't get it to work with query
public static class DapperExtensions {
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) {
var dapperResult = SqlMapper.Query<T>(cnn, sql, param, transaction, buffered, commandTimeout, commandType);
var result = TrimStrings(dapperResult.ToList());
return result;
}
static IEnumerable<T> TrimStrings<T>(IList<T> objects) {
//todo: create an Attribute that can designate that a property shouldn't be trimmed if we need it
var publicInstanceStringProperties = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType == typeof (string) && x.CanRead && x.CanWrite);
foreach (var prop in publicInstanceStringProperties) {
foreach (var obj in objects) {
var value = (string) prop.GetValue(obj);
var trimmedValue = value.SafeTrim();
prop.SetValue(obj, trimmedValue);
}
}
return objects;
}
static string SafeTrim(this string source) {
if (source == null) {
return null;
}
return source.Trim();
}
}
In the end i want to trim all string that come out of the database. At the moment i'm tunnelvisioning on the dapperextension, but if there is any better way. Please let me know.
If you are using Dapper version 1.50.2 you can do it in a simpler way.
Create a TypeHandler like the one bellow:
public class TrimmedStringHandler : SqlMapper.TypeHandler<string>
{
public override string Parse(object value)
{
string result = (value as string)?.Trim();
return result;
}
public override void SetValue(IDbDataParameter parameter, string value)
{
parameter.Value = value;
}
}
On the program initialization you must call the AddTypeHandler method of the SqlMapper class like bellow:
SqlMapper.AddTypeHandler(new TrimmedStringHandler());
If you do this, every string that come from database will be trimmed.

Add to a collection of unknown type using reflection in c#

So I am using reflection to loop through the properties of one object and populating the values on a different object with properties of the same name. This works great but the problem comes when the property type is a collection. I want to be able to loop through each of the objects in the source collection and populate the same list with objects in the source collection.
public class SourceMessage
{
public string Name { get; set; }
public int Version { get; set; }
public IList<ValueDefinition> Values { get; set; }
}
public class ValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
public class TargetObject
{
public TargetObject()
{
Values = new List<TargetValueDefinition>();
}
public string Name { get; set; }
public int Version { get; set; }
public IList<TargetValueDefinition> Values { get; set; }
}
public class TargetValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
Then I use Reflection to populate the target from the source.
public static void PopulateFromMessage<T, TS>(ref T targetEntity, TS message)
{
var sourceType = typeof(TS);
var targetType = typeof(T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
So calling this would be like this:
private void DenormalizeMessage(SourceMessage message)
{
var newTargetObject = new TargetObject();
PopulateFromMessage(ref newTargetObject , message);
}
I can identify when the property is a collection but am uncertain of how to create new TargetValueDefinitions and populate them with the values from ValueDefinitions. In the end it is pretty much a copy of the SourceMessage in the form of a TargetObject.
This all stems from receiving messages and transforming them into objects with the same property names.
If your problem is iterating through items contained inside a single property when it is a collection, then the key would be to read the property value into a dynamic variable and not an object variable that is by default, this way you could use a foreach for it.
dynamic propVal = inputProperty.GetValue(item);
foreach (var subItem in propVal)
{
//do your stuff
}
Disclaimer: This is extremely unsafe to do and makes a lot of assumptions but it should puth you on the right path.
Change you method to this:
public static void PopulateFromMessage<T, TS>(T targetEntity, TS message)
{
var sourceType = typeof (TS);
var targetType = typeof (T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (targetPropInfo.PropertyType.IsGenericType)
{
if (targetPropInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
{
var originalList = sourceType.GetProperty(targetPropInfo.Name).GetValue(message) as IList;
if (originalList != null)
{
var argumentType = targetPropInfo.PropertyType.GetGenericArguments();
var listType = typeof (List<>);
var concreteType = listType.MakeGenericType(argumentType);
var newList = Activator.CreateInstance(concreteType) as IList;
foreach (var original in originalList)
{
var targetValue = Activator.CreateInstance(argumentType[0]);
// do this yourself. Here we're converting ValueDefinition to TargetValueDefinition
// targetValue.Fill(original);
}
targetPropInfo.SetValue(targetEntity, newList);
}
}
}
else
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
}
You should create a interface for each class (implement the methods and properties on interface) and implement it in each class. After, in function PopulateFromMessage should specify the interface allowed in method, with this you can use directly the properties of class with T and TS generic types.

Clearing data before reading a new line

I have a considerable number of strings in my application that need to be cleared each time I get new data from my source. I'd like to use something akin to string.Empty, but I am unsure of how to implement this. Ideally, I'd also like to do this only once, rather than for each separate string.
Pseudo-code:
foreach (string in application)
{
this.empty
}
Am I thinking on the right track?
Some of my code is as follows:
classtoinstantiate
public string Str1;
private string str1 {get {return Str1;}}
public void DoStuff()
{
doStuff();
}
private void doStuff()
{
//dostuff
}
And Form1.cs
classtoinstantiate class1 = new classtoinstantiate();
class.DoStuff();
//I would like to then clear the *public* iteration of string Str1 here,
//before I DoStuff() again.
String.Empty represents a not null empty string.
If you want to clear a large amount of data (string/non string) you can encapsulate all of the variables in one class and create a Clean() method that goes through all the variables and clears them or instantiate that class when you need a fresh copy when you set the default values in the constructor.
The use of class.Empty is from what I understand to have a well defined instance of what is an empty instance.
Given your comments I get the feeling that you only want to clear the strings, have a look at this C# like pseudo code:
public void ClearString(IEnumerable<object> stuffToClear)
{
// go through all the objects to clear
foreach (var item in stuffToClear)
{
// get the properties to clear
var props = from prop in item.GetType().GetProperties()
where prop.PropertyType == typeof(string) // or another type or filter
select prop;
for (var p in props)
{
// clear it
p.SetValue(item, string.Empty);
}
}
}
Not that I'm writing this in freehand, all calls will surely not be correct.
That's the basic OOP concept: construct object when you need it, destroy at the end. Constructing part always deals with default values, which is exactly what you need.
For managed objects (string) simply create a new instance of a class holding all data what has to be reset (cleared):
class SomeDataStorage
{
// default is null
public string Data1 {get; set;}
private string _data2 = "default value";
public string Data2 { get {return _data2;} set {_data2 = value;}}
}
Then you construct this object when you need it
foreach (string in application)
{
var data = new SomeDataStorage(); // default values
...
}
It will be automagically destroyed when going out of scope (leaving { } or exiting function).
For unmanaged objects, implement IDisposable and consider to use using() { } often to auto-dispose.
You can have application-wide instance of SomeDataStorage. Simply assign a new object (construct new instance) to reset values to default.
To make it even more clear:
class App
{
public SomeDataStorage MyData;
public App()
{
Reset();
}
// call this when you need to init for the first time or simply reset to default
public void Reset()
{
MyData = new SomeDataStorage();
}
}
I suggest to put all your strings in to a class and dispose the object if you get new data
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
// .... more properties here
// this property won't be touched when clearing
public int SomeOtherProperty{ get; set; }
public void ClearStrings()
{
// returns all public properties
foreach (var prop in this.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(this, string.Empty, null); // <- "clear" value of the property
}
}
or, in a more general manner - use extension methods:
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
public int SomeOtherProperty { get; set; }
}
public class SomeOtherClass
{
public string a1 { get; set; }
public string a2 { get; set; }
public string a3 { get; set; }
public DateTime d1 { get; set; }
public int SomeOtherProperty { get; set; }
}
public static class MyExtensions
{
public static void ClearStrings(this Object obj)
{
// returns all public properties
foreach (var prop in obj.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(obj, string.Empty, null); // <- "clear" value of the property
}
}
}
use the code:
StringCollection scol2 = new StringCollection();
// ... do soemthing
scol2.ClearStrings();
SomeOtherClass obj = new SomeOtherClass();
// ... do something
obj.ClearStrings();

Categories