Using ValueInjecter, is there a way to only inject a given property one time - valueinjecter

Given 3 classes,
A, and B which each have an ID property, and then various other properties
and C, which has an ID, and the combined properties of A and B,
I want to
C.InjectFrom(A);
C.InjectFrom(B);
such that the ID from A is preserved and not overwritten by B.
Obviously in this simple case, I could just reverse the order of the two calls, but in my real world example, it is slightly more complicated where I cannot just solve the problem with ordering.
Esentially I want the second injection to ignore anything that the first injection has already handled, and this may be continued down a chain of several injections. Some of these injections may be from the same objects too
C.InjectFrom(A);
C.InjectFrom<SomeInjector>(A);
C.InjectFrom<SomeInjector2>(A);
C.InjectFrom<SomeInjector3>(A);
etc.

here you go:
using System;
using System.Collections.Generic;
using Omu.ValueInjecter;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var a = new { Id = 1, P1 = "p1" };
var b = new { Id = 2, P2 = "p2" };
var c = new C();
var propList = new List<string>();
c.InjectFrom(new HandlePropOnce(propList), a);
c.InjectFrom(new HandlePropOnce(propList), b);
Console.WriteLine("Id = {0} P1 = {1} P2 = {2}", c.Id, c.P1, c.P2);
}
}
public class C
{
public int Id { get; set; }
public string P1 { get; set; }
public string P2 { get; set; }
}
public class HandlePropOnce : ConventionInjection
{
private readonly IList<string> handledProps;
public HandlePropOnce(IList<string> handledProps)
{
this.handledProps = handledProps;
}
protected override bool Match(ConventionInfo c)
{
if (handledProps.Contains(c.SourceProp.Name)) return false;
var isMatch = c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Type == c.TargetProp.Type;
if (isMatch) handledProps.Add(c.SourceProp.Name);
return isMatch;
}
}
}

Related

csvHelper Sort field while writing

I am trying to use CsvHelper library to write CSV stream to a file. All is well however I am stuck with 2 things
How do I add additional columns to the csv file. The additional column value will be a constant but will only be known at runtime.
Is there a way to sort the CSV file on a particular field if I am writing one record at a time and also when I am writing all at once
static void Main(string[] args)
{
var records = new List<MyTest>
{
new MyTest { Id = 1, Name = "John", Extra = "hello" },
new MyTest { Id = 2, Name = "Jack", Extra = "hi" },
};
using (var writer = new StreamWriter("C:\\Users\\machina\\AppData\\Local\\Temp\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<TestMap<MyTest>>();
csv.WriteRecords<MyTest>(records);
}
}
}
public interface IData
{
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class AbTest : IData
{
public abstract int Id { get; set; }
public abstract string Name { get; set; }
public abstract string Extra { get; set; }
}
public class MyTest : AbTest
{
public override int Id { get; set; }
public override string Name { get; set; }
public override string Extra { get; set; }
}
public class TestMap<T> : ClassMap<MyTest>
{
public TestMap()
{
AutoMap(CultureInfo.InvariantCulture);
}
}
The above works as I have prepared the data records however what I want to achieve is to dynamically add Extra column values to my existing stream of csv data which does not have it.
Also I saw DynamicPropertySort under csvconfiguration and wanted to check if this can be used in any way to sort the fields in my CSV stream before its written even when its writing one record at a time?
I am a novoice developer so let me know if there is a better way to achieve this.
The easiest way to dynamically add columns would likely be to manually write out each row.
DynamicPropertySort is just used for ordering the columns of a dynamic object. You can sort on a particular field using the linq OrderBy() method.
static void Main(string[] args)
{
var records = new List<MyTest>
{
new MyTest { Id = 1, Name = "John", Extra = "hello" },
new MyTest { Id = 2, Name = "Jack", Extra = "hi" },
};
var extraField1 = 10;
var extraField2 = "Testing";
using (var writer = new StreamWriter("C:\\Users\\machina\\AppData\\Local\\Temp\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<TestMap>();
var orderedRecords = records.OrderBy(r => r.Name);
csv.WriteHeader<MyTest>();
csv.WriteField("ExtraField1");
csv.WriteField("ExtraField2");
csv.NextRecord();
foreach (var record in orderedRecords)
{
csv.WriteRecord(record);
csv.WriteField(extraField1);
csv.WriteField(extraField2);
csv.NextRecord();
}
}
}

Adding Left Border line in itextsharp

I was facing issue to work on rowspan but with some code in C#, I was able to achieve that. Currently I have the data what I needed.
How can I draw left border line for the first column so that it looks correct.
here is what I have now.
I need to add left border line.
here is my code:
{
ReportTableCell cell = null;
// prevent null values from crashing the system (always a good idea)
if (text == null)
{
text = string.Empty;
}
// else already valid
ReportTable.CreateReportTableCell(text, out cell);
cell.RowSpan = row_span;
cell.ColumnSpan = column_span;
cell.Style.WrapText = true;
cell.Style.BorderWidth = border_width;
// cell.Style.BorderColor = CellBorderColor.Gray;
if ( repeated)
{
// cell.Style.Border = BOTTOM_BORDER;
cell.Style.BorderWidth = border_width;
}
else
{
cell.Style.BorderWidth = 0;
repeated = true;
}
cell.Style.Font.FontStyle = font_style;
cell.Style.Font.FontSize = font_size;
cell.Style.BackgroundColor = background_color;
cell.Style.IsColumnHeader = is_column_header;
cell.Style.HorizontalAlignment = horizontal_alignment;
if (is_column_header)
{
cell.Style.VerticalAlignment = VerticalCellAlignment.Middle;
}
else
{
cell.Style.VerticalAlignment = VerticalCellAlignment.Top;
}
repeated = true;
return cell;
}
based on the condition i.e.
if ( repeated){}
I am removing border, which I know it has removed border around 4 corners, how can i append an extra line only for the left side.
UPDATE : 1
after using the code as below :
if ( repeated)
{
cell.Style.BorderWidth = border_width;
}
else
{
cell.Style.BorderWidth = 0;
cell.Style.Border = BOTTOM_BORDER | LEFT_BORDER;
repeated = true;
}
changed nothing!.
Here is the base class of the report.
public class ReportTableCell
{
public static readonly int ALL_BORDER;
public static readonly int BOTTOM_BORDER;
public static readonly int LEFT_BORDER;
public static readonly int NO_BORDER;
public static readonly int RIGHT_BORDER;
public static readonly int TOP_BORDER;
public bool AllowBlanks { get; set; }
public int ColumnSpan { get; set; }
public ReportFont Font { get; set; }
public int RowSpan { get; set; }
public TableCellStyle Style { get; set; }
public ReportTable Table { get; set; }
public object Value { get; set; }
public void AddImage(string FileName);
}
UPDATE : 2
I have changed the code as you suggested but , I just have to add single border line to the last. this is what i achieved till now :

How to send complex objects in GET to WEB API 2

Let's say that you have the following code
public class MyClass {
public double Latitude {get; set;}
public double Longitude {get; set;}
}
public class Criteria
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public MyClass MyProp {get; set;}
}
[HttpGet]
public Criteria Get([FromUri] Criteria c)
{
return c;
}
I'd like to know if someone is aware of a library that could transform any object into query string that is understood by a WEB API 2 Controller.
Here is an example of what I'd like
SerializeToQueryString(new Criteria{StartDate=DateTime.Today, EndDate = DateTime.Today.AddDays(1), MyProp = new MyProp{Latitude=1, Longitude=3}});
=> "startDate=2015-10-13&endDate=2015-10-14&myProp.latitude=1&myProp.longitude=3"
A full example with httpClient might look like :
new HttpClient("http://localhost").GetAsync("/tmp?"+SerializeToQueryString(new Criteria{StartDate=DateTime.Today, EndDate = DateTime.Today.AddDays(1), MyProp = new MyProp{Latitude=1, Longitude=3}})).Result;
At the moment, I use a version (taken from a question I do not find again, maybe How do I serialize an object into query-string format? ...).
The problem is that it is not working for anything else than simple properties.
For example, calling ToString on a Date will not give something that is parseable by WEB API 2 controller...
private string SerializeToQueryString<T>(T aObject)
{
var query = HttpUtility.ParseQueryString(string.Empty);
var fields = typeof(T).GetProperties();
foreach (var field in fields)
{
string key = field.Name;
var value = field.GetValue(aObject);
if (value != null)
query[key] = value.ToString();
}
return query.ToString();
}
"Transform any object to a query string" seems to imply there's a standard format for this, and there just isn't. So you would need to pick one or roll your own. JSON seems like the obvious choice due to the availability of great libraries.
Since it seems no one has dealt with the problem before, here is the solution I use in my project :
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Web;
namespace App
{
public class QueryStringSerializer
{
public static string SerializeToQueryString(object aObject)
{
return SerializeToQueryString(aObject, "").ToString();
}
private static NameValueCollection SerializeToQueryString(object aObject, string prefix)
{
//!\ doing this to get back a HttpValueCollection which is an internal class
//we want a HttpValueCollection because toString on this class is what we want in the public method
//cf http://stackoverflow.com/a/17096289/1545567
var query = HttpUtility.ParseQueryString(String.Empty);
var fields = aObject.GetType().GetProperties();
foreach (var field in fields)
{
string key = string.IsNullOrEmpty(prefix) ? field.Name : prefix + "." + field.Name;
var value = field.GetValue(aObject);
if (value != null)
{
var propertyType = GetUnderlyingPropertyType(field.PropertyType);
if (IsSupportedType(propertyType))
{
query.Add(key, ToString(value));
}
else if (value is IEnumerable)
{
var enumerableValue = (IEnumerable) value;
foreach (var enumerableValueElement in enumerableValue)
{
if (IsSupportedType(GetUnderlyingPropertyType(enumerableValueElement.GetType())))
{
query.Add(key, ToString(enumerableValueElement));
}
else
{
//it seems that WEB API 2 Controllers are unable to deserialize collections of complex objects...
throw new Exception("can not use IEnumerable<T> where T is a class because it is not understood server side");
}
}
}
else
{
var subquery = SerializeToQueryString(value, key);
query.Add(subquery);
}
}
}
return query;
}
private static Type GetUnderlyingPropertyType(Type propType)
{
var nullablePropertyType = Nullable.GetUnderlyingType(propType);
return nullablePropertyType ?? propType;
}
private static bool IsSupportedType(Type propertyType)
{
return SUPPORTED_TYPES.Contains(propertyType) || propertyType.IsEnum;
}
private static readonly Type[] SUPPORTED_TYPES = new[]
{
typeof(DateTime),
typeof(string),
typeof(int),
typeof(long),
typeof(float),
typeof(double)
};
private static string ToString(object value)
{
if (value is DateTime)
{
var dateValue = (DateTime) value;
if (dateValue.Hour == 0 && dateValue.Minute == 0 && dateValue.Second == 0)
{
return dateValue.ToString("yyyy-MM-dd");
}
else
{
return dateValue.ToString("yyyy-MM-dd HH:mm:ss");
}
}
else if (value is float)
{
return ((float) value).ToString(CultureInfo.InvariantCulture);
}
else if (value is double)
{
return ((double)value).ToString(CultureInfo.InvariantCulture);
}
else /*int, long, string, ENUM*/
{
return value.ToString();
}
}
}
}
Here is the unit test to demonstrate :
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Framework.WebApi.Core.Tests
{
[TestClass]
public class QueryStringSerializerTest
{
public class EasyObject
{
public string MyString { get; set; }
public int? MyInt { get; set; }
public long? MyLong { get; set; }
public float? MyFloat { get; set; }
public double? MyDouble { get; set; }
}
[TestMethod]
public void TestEasyObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject(){MyString = "string", MyInt = 1, MyLong = 1L, MyFloat = 1.5F, MyDouble = 1.4});
Assert.IsTrue(queryString.Contains("MyString=string"));
Assert.IsTrue(queryString.Contains("MyInt=1"));
Assert.IsTrue(queryString.Contains("MyLong=1"));
Assert.IsTrue(queryString.Contains("MyFloat=1.5"));
Assert.IsTrue(queryString.Contains("MyDouble=1.4"));
}
[TestMethod]
public void TestEasyObjectNullable()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject() { });
Assert.IsTrue(queryString == "");
}
[TestMethod]
public void TestUrlEncoding()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject() { MyString = "&=/;+" });
Assert.IsTrue(queryString.Contains("MyString=%26%3d%2f%3b%2b"));
}
public class DateObject
{
public DateTime MyDate { get; set; }
}
[TestMethod]
public void TestDate()
{
var d = DateTime.ParseExact("2010-10-13", "yyyy-MM-dd", CultureInfo.InvariantCulture);
var queryString = QueryStringSerializer.SerializeToQueryString(new DateObject() { MyDate = d });
Assert.IsTrue(queryString.Contains("MyDate=2010-10-13"));
}
[TestMethod]
public void TestDateTime()
{
var d = DateTime.ParseExact("2010-10-13 20:00", "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
var queryString = QueryStringSerializer.SerializeToQueryString(new DateObject() { MyDate = d });
Assert.IsTrue(queryString.Contains("MyDate=2010-10-13+20%3a00%3a00"));
}
public class InnerComplexObject
{
public double Lat { get; set; }
public double Lon { get; set; }
}
public class ComplexObject
{
public InnerComplexObject Inner { get; set; }
}
[TestMethod]
public void TestComplexObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new ComplexObject() { Inner = new InnerComplexObject() {Lat = 50, Lon = 2} });
Assert.IsTrue(queryString.Contains("Inner.Lat=50"));
Assert.IsTrue(queryString.Contains("Inner.Lon=2"));
}
public class EnumerableObject
{
public IEnumerable<int> InnerInts { get; set; }
}
[TestMethod]
public void TestEnumerableObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EnumerableObject() {
InnerInts = new[] { 1,2 }
});
Assert.IsTrue(queryString.Contains("InnerInts=1"));
Assert.IsTrue(queryString.Contains("InnerInts=2"));
}
public class ComplexEnumerableObject
{
public IEnumerable<InnerComplexObject> Inners { get; set; }
}
[TestMethod]
public void TestComplexEnumerableObject()
{
try
{
QueryStringSerializer.SerializeToQueryString(new ComplexEnumerableObject()
{
Inners = new[]
{
new InnerComplexObject() {Lat = 50, Lon = 2},
new InnerComplexObject() {Lat = 51, Lon = 3},
}
});
Assert.Fail("we should refuse something that will not be understand by the server");
}
catch (Exception e)
{
Assert.AreEqual("can not use IEnumerable<T> where T is a class because it is not understood server side", e.Message);
}
}
public enum TheEnum : int
{
One = 1,
Two = 2
}
public class EnumObject
{
public TheEnum? MyEnum { get; set; }
}
[TestMethod]
public void TestEnum()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EnumObject() { MyEnum = TheEnum.Two});
Assert.IsTrue(queryString.Contains("MyEnum=Two"));
}
}
}
I'd like to thank all the participants even if this is not something that you should usually do in a Q&A format :)

Xamarin.Android: item previously inserted in ArrayAdapter is not found again

I've inherited this Xamarin.Android app and it has a few issues.
A particular bug involves an ArrayAdapter<ProductListObject>, where ProductListObject is a common POCO that's shared between subprojects (i.e. Android, Windows Phone and iOS); it just has a couple of properties (e.g. an Id) and overrides the (.NET) Equals() method to achieve structural equality:
public class ProductListObject
{
public long Id { get; set; }
public override bool Equals(object obj)
{
if (!(obj is ProductListObject))
{
return false;
}
return Id == (obj as ProductListObject).Id;
}
}
The problem is that whenever I put an instance of this ProductListObject in an ArrayAdapter, I can't find it again, even if they have the same Id:
var p1 = new ProductListObject { Id = 1 };
var p2 = new ProductListObject { Id = 1 };
var areEqual = p1.Equals(p2); // returns True, as expected
var productAdapter = new ArrayAdapter<ProductListObject>(this, 0, new[] { p1 });
var position = productAdapter.GetPosition(p2); // returns -1 >:(
My question is: what do I have to do to make my POCO's work with Xamarin.Android types that rely on the Java equals() method, internally (like ArrayAdapter; which delegates to List.indexOf(Object))?
What I have tried:
verified that the corresponding Java version works as expected (it does)
overrode GetHashCode() (it doesn't matter, as I expected)
googled and checked the Xamarin documentation for information about implementing Equals() (I found nothing particularly relevant)
Thanks,
Jan
I did as Matt R suggested and created a proxy that inherits from Java.Lang.Object and delegates to the actual .NET object:
public class JavaObject<TValue> : Java.Lang.Object
{
public readonly TValue Value;
internal JavaObject(TValue value)
{
Value = value;
}
public override bool Equals(Java.Lang.Object that)
{
if (!(that is JavaObject<TValue>))
{
return false;
}
return Value.Equals((that as JavaObject<TValue>).Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
This doesn't tie my platform-agnostic POCO's to the Android implementation, plus it doesn't force me to lock into some rigid inheritance tree, which is always a plus.
Application is straightforward:
var p1 = new JavaObject<ProductListObject>(new ProductListObject { Id = 1 });
var p2 = new JavaObject<ProductListObject>(new ProductListObject { Id = 1 });
var areEqual = p1.Equals(p2); // returns True, as expected
var productAdapter = new ArrayAdapter<JavaObject<ProductListObject>>(this, 0, new[] { p1 });
var position = productAdapter.GetPosition(p2); // returns 0!
It looks like .NET objects get wrapped within a Java.Lang.Object when used inside a Android.Widget.ArrayAdapter. Therefore, the comparison method that's used in the productAdapter.GetPosition(...) call is actually the java Equals(Java.Lang.Object o) method for the wrapping Java.Lang.Object.
To make a ProductListObject resolve to the same index when two objects have the same Id, make ProductListObject derive from Java.Lang.Object, override the Equals(Java.Lang.Object) and forward it to the .NET Equals(System.Object) method:
public class ProductListObject : Java.Lang.Object
{
public long Id { get; set; }
public override bool Equals(object obj) // Inherited from System.Object.
{
if (!(obj is ProductListObject))
{
return false;
}
return Id == (obj as ProductListObject).Id;
}
public override bool Equals (Java.Lang.Object o) // Inherited from Java.Lang.Object.
{
return this.Equals (o as System.Object);
}
}
If you can't inherit ProductListObject from Java.Lang.Object, another option is to implement your own proxy class:
public class ProductListObject
{
public long Id { get; set; }
public override bool Equals(System.Object obj)
{
if (!(obj is ProductListObject))
{
return false;
}
return Id == (obj as ProductListObject).Id;
}
}
public class JavaProxy: Java.Lang.Object
{
public Object Object { get; private set; }
public JavaProxy(System.Object o)
{
Object = o;
}
public override bool Equals (Java.Lang.Object o)
{
var proxy = o as JavaProxy;
if (o != null) {
return Object.Equals (proxy.Object);
}
return base.Equals (o);
}
}
// ...
var productAdapter = new ArrayAdapter<JavaProxy>(this, 0, new[] { new JavaProxy(p1) });
var position = productAdapter.GetPosition(new JavaProxy(p2));
It's not as clean as the first approach but it also works.

Key comparisons for Linq GroupBy using Default EqualityComparer

I'm trying to do a Linq GroupBy on some objects using an explicit key type. I'm not passing an IEqualityComparer to the GroupBy, so according to the docs:
The default equality comparer Default is used to compare keys.
It explains the EqualityComparer<T>.Default property like this:
The Default property checks whether
type T implements the
System.IEquatable<T> generic interface
and if so returns an
EqualityComparer<T> that uses that
implementation.
In the code below, I'm grouping an array of Fred objects. They have a key type called FredKey, which implements IEquatable<FredKey>.
That should be enough to make the grouping work, but the grouping is not working. In the last line below I should have 2 groups, but I don't, I just have 3 groups containing the 3 input items.
Why is the grouping not working?
class Fred
{
public string A;
public string B;
public FredKey Key
{
get { return new FredKey() { A = this.A }; }
}
}
class FredKey : IEquatable<FredKey>
{
public string A;
public bool Equals(FredKey other)
{
return A == other.A;
}
}
class Program
{
static void Main(string[] args)
{
var f = new Fred[]
{
new Fred {A = "hello", B = "frog"},
new Fred {A = "jim", B = "jog"},
new Fred {A = "hello", B = "bog"},
};
var groups = f.GroupBy(x => x.Key);
Debug.Assert(groups.Count() == 2); // <--- fails
}
}
From MSDN
If you implement IEquatable, you should also override the base class implementations of Object::Equals(Object) and GetHashCode() so that their behavior is consistent with that of the IEquatable::Equals method. If you do override Object::Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. This ensures that all invocations of the Equals() method return consistent results.
add this to FredKey and it should work
public override int GetHashCode()
{
return A.GetHashCode();
}
Here is a complete example with a Fiddle. Note: the example differs slightly from the question's example.
The following implementation of IEquatable can act as the TKey in GroupBy. Note that it includes both GetHashCode and Equals.
public class CustomKey : IEquatable<CustomKey>
{
public string A { get; set; }
public string B { get; set; }
public bool Equals(CustomKey other)
{
return other.A == A && other.B == B;
}
public override int GetHashCode()
{
return string.Format("{0}{1}", A, B).GetHashCode();
}
}
public class Custom
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
public static void Main()
{
var c = new Custom[]
{
new Custom {A = "hello", B = "frog" },
new Custom {A = "jim", B = "jog" },
new Custom {A = "hello", B = "frog" },
};
var groups = c.GroupBy(x => new CustomKey { A = x.A, B = x.B } );
Console.WriteLine(groups.Count() == 2);
}

Resources