Calling a particular implementation inside a generic method - c#

I'm currently trying to implement a generic method to adapt a DTO that comes from an external service into a model of my servisse and I ran into an issue.
First let me just contextualize the problem.
Let's say that an external service returns the following DTO to my application.
public class ExampleDTO
{
public int Field1 { get; set; }
public int Field2 { get; set; }
}
And this is my model.
public class ExampleModel
{
public int Field1 { get; set; }
public int Field2 { get; set; }
}
If I wanted to adapt the first class into my model I could just simply write the following method:
public ExampleModel Adapt(ExampleDTO source)
{
if (source == null) return null;
var target = new ExampleModel()
{
Field1 = source.Field1,
Field2 = source.Field2
};
return target;
}
Now let's say that when we get a collection of ExampleDTOs, the service instead of just returning a collection of type ICollection, returns the following class:
public class PagedCollectionResultDTO<T>
{
public List<T> Data { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public int Total { get; set; }
}
Where the list of ExampleDTOs comes on the Data field and the page number, page size and total number of records comes on the rest of the fields.
I'm trying to implement a generic method to adapt this class to my own model, which has the same structure. I want to do this independently of the type T of the Data field.
public class PagedCollectionResult<T>
{
public List<T> Data { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public int Total { get; set; }
public PagedCollectionResult() => (Data, Page, PageSize, Total) = (new List<T>(), 0, 0, 0);
}
I've tried the following method, where I try to adapt the DTO paged result (S) into the model paged result (T) :
public PagedCollectionResult<T> Adapt<S,T>(PagedCollectionResultDTO<S> source)
{
if (source == null) return null;
var target = new PagedCollectionResult<T>();
foreach (var item in source.Data)
target.Data.Add(this.Adapt(item));
target.Page = source.Page;
target.PageSize = source.PageSize;
target.Total = source.Total;
return target;
}
The thing is that I'm getting an error in the line:
target.Data.Add(this.Adapt(item));
It says that it can't convert S into ExampleDTO.
If I put a restriction for ExampleDTO/ExampleModel on Adapt this isn't generic anymore.
Is there a way to call the Adapt(item) method for the specific type?
This is my complete TypeAdapter:
public class TypeAdapter
{
public PagedCollectionResult<T> Adapt<S,T>(PagedCollectionResultDTO<S> source)
{
if (source == null) return null;
var target = new PagedCollectionResult<T>();
foreach (var item in source.Data)
target.Data.Add(this.Adapt(item));
target.Page = source.Page;
target.PageSize = source.PageSize;
target.Total = source.Total;
return target;
}
public ExampleModel Adapt(ExampleDTO source)
{
if (source == null) return null;
var target = new ExampleModel()
{
Field1 = source.Field1,
Field2 = source.Field2
};
return target;
}
}

As I understand it, the solution is broken into two parts, that should work independently from one another.
Part A: generic pattern for object transformation
Create an interface to be implemented by all your models that need to be adapted from an external DTO.
public interface IExampleModel<S>
{
void Adapt(S source);
}
This interface requires only an Adapt method and the generic type S describes the type to adapt from.
Now build a class for each model that you want to adapt from another type.
public class ExampleModel : IExampleModel<ExampleDTO>
{
public int Field1 { get; set; }
public int Field2 { get; set; }
public void Adapt(ExampleDTO source)
{
Field1 = source.Field1;
Field2 = source.Field2;
}
}
Your TypeAdapter class:
public class TypeAdapter
{
public PagedCollectionResult<T> Adapt<S,T>(PagedCollectionResultDTO<S> source)
where T: IExampleModel<S>, new()
{
var target = new PagedCollectionResult<T>();
target.Page = source.Page;
target.Page = source.PageSize;
target.Total = source.Total;
target.Data = AdaptList<S,T>(source.Data).ToList();
return target;
}
protected IEnumerable<T> AdaptList<S,T>(IEnumerable<S> sourceList)
where T : IExampleModel<S>, new()
{
foreach (S sourceItem in sourceList)
{
T targetItem = new T();
targetItem.Adapt(sourceItem);
yield return targetItem;
}
}
}
NOTE 1: the PagedCollectionResult constructor is not required in this approach.
NOTE 2: I chose to put the Adapt method in each model class rather than in the TypeAdapter to satisfy the open-closed principle. This way when you want to extend the solution in order to accept a second type transformation, you don't modify any existing classes (i.e. the TypeModifier), but add instead a new class.
public class ExampleModel2 : IExampleModel<ExampleDTO2>
{
public int Field1 { get; set; }
public int Field2 { get; set; }
public void Adapt(ExampleDTO2 source)
{
Field1 = source.Field1;
Field2 = source.Field2;
}
}
Adding this class will extend the solution to include this transformation as well.
Ofcourse you can choose your own way if it suits your application better.
Part B: the service call
There isn't a way, that I know of, for the application to extract the target type, given only the source type. You must provide it in the service call. You have no description on what your service call looks like, so I will just give you some hints.
If your service call is something like this it would work:
public void ServiceCall<S,T>(PagedCollectionResultDTO<S> sourceCollection)
where T : IExampleModel<S>, new()
{
var typeAdapter = new TypeAdapter();
var targetCollection = typeAdapter.Adapt<S,T>(sourceCollection);
}
If passing the T type in the service call is not possible you might use an if-clause to properly define the target type:
public void ServiceCall<S>(PagedCollectionResultDTO<S> sourceCollection)
{
var typeAdapter = new TypeAdapter();
if (typeof(S) == typeof(ExampleDTO))
{
var targetCollection = typeAdapter.Adapt<ExampleDTO, ExampleModel>(sourceCollection as PagedCollectionResultDTO<ExampleDTO>);
}
else if(typeof(S) == typeof(ExampleDTO2))
{
var targetCollection = typeAdapter.Adapt<ExampleDTO2, ExampleModel2>(sourceCollection as PagedCollectionResultDTO<ExampleDTO2>);
}
}

You have two choices here,
1. Generate list of properties of Model and Dto via reflection. and then match their types.
class AdapterHelper<T1, T2>
{
public T1 Adapt(T2 source)
{
T1 targetItem = Activator.CreateInstance<T1>();
var props = typeof(T1).GetProperties();
var targetProps = typeof(T2).GetProperties();
foreach (var prop in props)
{
foreach (var targetProp in targetProps)
{
if (prop.Name == targetProp.Name)
{
targetProp.SetValue(targetItem, prop.GetValue(source));
//assign
}
}
}
return targetItem;
}
}
2.Use Automapper

Because you are implementing a generic method, you need to either implement a generic method for transforming S to T (see other answers), or you need to pass in the transformation function.
public PagedCollectionResult<T> Adapt<S, T>(PagedCollectionResultDTO<S> source, Func<S, T> adapt)
{
if (source == null) return null;
var target = new PagedCollectionResult<T>();
foreach (var item in source.Data)
target.Data.Add(adapt(item));
target.Page = source.Page;
target.PageSize = source.PageSize;
target.Total = source.Total;
return target;
}
Here is example code to call the above method.
static void Main(string[] args)
{
var src = new PagedCollectionResultDTO<ExampleDTO>();
src.Data = new List<ExampleDTO>{
new ExampleDTO{ Field1 = 1, Field2 = 2 }
};
var adapter = new TypeAdapter();
var result = adapter.Adapt(src, AdaptExampleDTO);
}
public static ExampleModel AdaptExampleDTO(ExampleDTO source)
{
if (source == null) return null;
var target = new ExampleModel()
{
Field1 = source.Field1,
Field2 = source.Field2
};
return target;
}

Thank you all for your responses, they helped me getting in the right direction.
I used reflection at runtime to resolve the right adapt method.
I learned a few things about reflection thanks to you.
I'm sharing the solution in hope I can also give something back.
This is what I ended up with.
public PagedCollectionResult<T> Adapt<S, T>(PagedCollectionResultDTO<S> source)
{
if (source == null)
{
return null;
}
var target = new PagedCollectionResult<T>();
// Get the type of S at runtime
Type[] types = { typeof(S) };
// Find the Adapt method on the TypeAdapter class that accepts an object of type S
var adaptMethod = typeof(TypeAdapter).GetMethod("Adapt", types);
foreach (var item in source.Data)
{
// for each item call the adapt method previously resolved and pass the item as parameter
var parameters = new object[] { item };
target.Data.Add((T)adaptMethod.Invoke(this, parameters));
}
target.Page = source.Page;
target.PageSize = source.PageSize;
target.Total = source.Total;
return target;
}

Related

Convert object by reflection

I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.
I use this method:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.
This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.
If you're trying to "shape" the object maybe just passing an interface will do the work
Anyhow, see if this helps:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Usage example:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
Optionally you can set a property match for the case when properties doesn't have the exact same name.
Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)
You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
And then a single line to resolve the mapping when needed
mapper.Map(objectOfClassA, new ClassB());

How to fill some properties in model after evaluation of IQueryable in OData?

I'm using OData with Entity Framework. Let's assume that I have following models and controller method:
public class Model1
{
public int Id { get; set; }
public int Field2 { get; set; }
public int FieldFromOtherService { get; set; }
public Model2 Model2 { get; set; } // Navigation Property
public int Model2Id { get; set; }
}
public class Model2
{
public int Id { get; set; }
public int Field { get; set; }
}
[HttpGet, EnableQuery]
public IQueryable<Model1> Get()
{
return modelRepository.List();
}
Model1 has property FieldFromOtherService that is not taken from DB - it is retrieved from other service. I need a way to fill this property after applying OData top, skip, expand and select clause.
Is there a way to accomplish that? I've tried to make a wrapper to IQueryable and call action after evaluation but it crash when query is more complicated.
Finally, I manage to accomplish my goals with #zaitsman suggestion. It was harder then I thought because OData adds wrappers that are not accessible (classes SelectAllAndExpand, SelectAll, SelectSomeAndInheritance, SelectSome). When expand is used, it is necessary to extract DTO from the wrapper. My code looks more or less like this:
[HttpGet]
public IHttpActionResult Get(ODataQueryOptions<Model1> options)
{
var result = modelRepository.List();
Action<ICollection<Model1>> postAction = collection => { Console.WriteLine("Post Action"); };
return ApplyOdataOptionsAndCallPostAction(result, options, postAction);
}
private IHttpActionResult ApplyOdataOptionsAndCallPostAction<T>(
IQueryable<T> baseQuery,
ODataQueryOptions<T> options,
Action<ICollection<T>> postAction)
where T : class
{
var queryable = options.ApplyTo(baseQuery);
var itemType = queryable.GetType().GetGenericArguments().First();
var evaluatedQuery = ToTypedList(queryable, itemType);
var dtos = ExtractAllDtoObjects<T>(evaluatedQuery).ToList();
postAction(dtos)
return Ok(evaluatedQuery, evaluatedQuery.GetType());
}
private static IList ToTypedList(IEnumerable self, Type innerType)
{
var methodInfo = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList));
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new object[]
{
self
}) as IList;
}
private IEnumerable<T> ExtractAllDtoObjects<T>(IEnumerable enumerable)
where T : class
{
foreach (var item in enumerable)
{
if (item is T typetItem)
{
yield return typetItem;
}
else
{
var result = TryExtractTFromWrapper<T>(item);
if (result != null)
{
yield return result;
}
}
}
}
private static T TryExtractTFromWrapper<T>(object item)
where T : class
{
if (item is ISelectExpandWrapper wrapper)
{
var property = item.GetType().GetProperty("Instance");
var instance = property.GetValue(item);
if (instance is T val)
{
return val;
}
}
return null;
}
private IHttpActionResult Ok(object content, Type type)
{
var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}

How can I set a default value inside of a class as a property of another class?

I need to set a property inside of a class from another class that defines the first class as a property. I want to default a value inside the child class. An example of this would be:
public enum NamingConvention
{
Name1 = 1,
Name2
}
public class Class1
{
public Class1()
{
}
public int Id { get; set; }
public NamingConvention Naming{ get; set; }
}
public class Class2
{
public Class2()
{
}
public List<Class1> Name1s { get; set; }
}
public class Class3
{
public Class2()
{
}
public List<Class1> Name2s { get; set; }
}
I want to be able to put an attribute or something over the Class1 property inside of Class2 and Class3 so that in Class2, the Naming Property gets set to Name1 and and for Class3, it would be automatically set to Name2.
Hope that makes sense. I tried to make this as simple an example as possible. Any ideas out there? I am trying to avoid abstract classes because my real entities are tied to nHibernate and don't want to change the model at this time.
This can be accomplished with the use of the DefaultValueAttribute, a custom TypeConverter and Reflection. It seems unlikely this will perform better than what you are currently doing, but I'll leave that for you to evaluate.
Apply the TypeConverter attribute to Class 1
[TypeConverter(typeof(Class1Converter))]
public class Class1
{
public int Id { get; set; }
public NamingConvention Naming { get; set; }
}
public enum NamingConvention
{
Name1 = 1,
Name2,
Name3,
Name4
}
Define the Class1Converter. Note this simple converter only sets the value of the NamingConvention parameter.
public class Class1Converter: TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destinationType)
{
if(destinationType == typeof(Class1))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
var stringValue = value as string;
if(stringValue != null)
{
return new Class1
{
Naming = (NamingConvention)Enum.Parse(typeof(NamingConvention), stringValue)
};
}
return base.ConvertFrom(context, culture, value);
}
}
For convenience I am declaring this in an Extension Method, it could easily be set up as part of the classes with defaults...
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
Finally you declare place DefaultValue attributes on your properties. I am calling SetDefaults() from the constructors here for convenience again, in your case you would still need to call it after the instances are loaded from NHibernate.
public class Class2
{
public int X { get; set; }
[DefaultValue(typeof(Class1), "Name2")]
public Class1 Name2Class { get; set; }
public Class2()
{
this.SetDefaults();
}
}
public class Class3
{
public int Y { get; set; }
[DefaultValue(typeof(Class1), "Name3")]
public Class1 Name3Class { get; set; }
public Class3()
{
this.SetDefaults();
}
}
Unit test demonstrating validity...
[Test]
public void TestDefaultValueAttribute()
{
//Class2 have Name2 as the default value for the Naming property
var c2 = new Class2();
Assert.That(c2,Is.Not.Null);
Assert.That(c2.Name2Class, Is.Not.Null);
Assert.That(c2.Name2Class.Naming, Is.EqualTo(NamingConvention.Name2));
//Class3 have Name3 as the default value for the Naming Property
var c3 = new Class3();
Assert.That(c3, Is.Not.Null);
Assert.That(c3.Name3Class, Is.Not.Null);
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
//wipes out other properties of the Class1 attribute.
// to demonstrate, set properties to something other than the default then call
// SetDefaults again.
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 10;
c3.SetDefaults();
Assert.That(c3.Name3Class.Id, Is.EqualTo(0));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
You will notice that this wipes out the Id property of Class1 If this is not desired, you could come up with a more targeted version of SetDefaults that only overwrote specific properties of Class1. At this point I don't know if I would really continue using DefaultValue, as use case deviates from the original and using this in combination with the above method would produce unexpected results. I would probably write a custom 'DefaultNaminingConventionAttribute for this purpose.
public static void SetDefaultNamingConvention<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<Class1>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as Class1;
if (pValue != null)
{
pValue.Naming = ((Class1)dv.Value).Naming;
}
else
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
[Test]
public void SetDefaultNamingConventionDefaultShouldOnlyDefaultNamingProperty()
{
var c3 = new Class3();
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 20;
c3.SetDefaultNamingConvention();
Assert.That(c3.Name3Class.Id, Is.EqualTo(20));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
EDIT: Updated to deal with setting defaults for list members
With this new SetListDefaults extension method, we now can apply the default to members of List<Class1>. Here I would almost definitely no longer use DefaultValue, but would define a custom attribute for use with collections. This is beyond the scope of the question though.
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetListDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<List<Class1>>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as List<Class1>;
if (pValue != null)
{
foreach (var class1 in pValue)
{
class1.Naming = ((Class1) dv.Value).Naming;
}
}
}
}
}
}
Now provided a class with a List property...
public class Class4
{
public int Z { get; set; }
[DefaultValue(typeof (Class1), "Name4")]
public List<Class1> Name4Classes { get; set; }
}
And a unit test to verify only the Naming Property of each item in the list is modified.
[Test]
public void SetListDefaultsShouldResetNamingConventionOfEachListMember()
{
var c4 = new Class4
{
Z = 100,
Name4Classes = new List<Class1>
{
new Class1 {Id = 1, Naming = NamingConvention.Name1},
new Class1 {Id = 2, Naming = NamingConvention.Name2},
new Class1 {Id = 3, Naming = NamingConvention.Name3}
}
};
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c => c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.Any(c => c.Naming == NamingConvention.Name4), Is.False);
c4.SetListDefaults();
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c=> c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.All(c=> c.Naming == NamingConvention.Name4), Is.True);
}
I would use the constructors.
In Class2's constructor:
public Class2()
{
Name1Class = new Class1()
Name1Class.Naming = NamingConvention.Name1
}
In Class3's Constructor:
public Class3()
{
Name2Class = new Class1()
Name2Class.Naming = NamingConvention.Name2
}
If you want to get fancy you could put a parameter on the constructor in Class1 to allow you to set Naming when the object is created.

Remove the null property from object

,I have one class in which I have three properties now what I want to do, if in the object if any one of null or empty then I want to remove it from the object below is my code.
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
Now here I want the object of TestClass but in that Name and DateTime property should not be their in the object,
is it possible?
Please help me
There's no such concept as removing a property from an individual object. The type decided which properties are present - not individual objects.
In particular, it will always be valid to have a method like this:
public void ShowDateTime(TestClass t)
{
Console.WriteLine(t.DateTme);
}
That code has no way of knowing whether you've wanted to "remove" the DateTime property from the object that t refers to. If the value is null, it will just get that value - that's fine. But you can't remove the property itself.
If you're listing the properties of an object somewhere, you should do the filtering there, instead.
EDIT: Okay, no you've given us some context:
ok I am using Schemaless database so null and empty value also store space in database that's the reason
So in the code you're using which populates that database, just don't set any fields which corresponds to properties with a null value. That's purely a database population concern - not a matter for the object itself.
(I'd also argue that you should consider how much space you'll really save by doing this. Do you really care that much?)
I was bored and got this in LINQPad
void Main()
{
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
t.Dump();
var ret = t.FixMeUp();
((object)ret).Dump();
}
public static class ReClasser
{
public static dynamic FixMeUp<T>(this T fixMe)
{
var t = fixMe.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach(var pr in t.GetProperties())
{
var val = pr.GetValue(fixMe);
if(val is string && string.IsNullOrWhiteSpace(val.ToString()))
{
}
else if(val == null)
{
}
else
{
returnClass.Add(pr.Name, val);
}
}
return returnClass;
}
}
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Hereby a 'slightly' more clear and shorter version of the accepted answer.
/// <returns>A dynamic object with only the filled properties of an object</returns>
public static object ConvertToObjectWithoutPropertiesWithNullValues<T>(this T objectToTransform)
{
var type = objectToTransform.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach (var propertyInfo in type.GetProperties())
{
var value = propertyInfo.GetValue(objectToTransform);
var valueIsNotAString = !(value is string && !string.IsNullOrWhiteSpace(value.ToString()));
if (valueIsNotAString && value != null)
{
returnClass.Add(propertyInfo.Name, value);
}
}
return returnClass;
}
You could take advantage of the dynamic type:
class Program
{
static void Main(string[] args)
{
List<dynamic> list = new List<dynamic>();
dynamic
t1 = new ExpandoObject(),
t2 = new ExpandoObject();
t1.Address = "address1";
t1.ID = 132;
t2.Address = "address2";
t2.ID = 133;
t2.Name = "someName";
t2.DateTime = DateTime.Now;
list.AddRange(new[] { t1, t2 });
// later in your code
list.Select((obj, index) =>
new { index, obj }).ToList().ForEach(item =>
{
Console.WriteLine("Object #{0}", item.index);
((IDictionary<string, object>)item.obj).ToList()
.ForEach(i =>
{
Console.WriteLine("Property: {0} Value: {1}",
i.Key, i.Value);
});
Console.WriteLine();
});
// or maybe generate JSON
var s = JsonSerializer.Create();
var sb=new StringBuilder();
var w=new StringWriter(sb);
var items = list.Select(item =>
{
sb.Clear();
s.Serialize(w, item);
return sb.ToString();
});
items.ToList().ForEach(json =>
{
Console.WriteLine(json);
});
}
}
May be interfaces will be handy:
public interface IAdressAndId
{
int ID { get; set; }
string Address { get; set; }
}
public interface INameAndDate
{
string Name { get; set; }
DateTime? DateTime { get; set; }
}
public class TestClass : IAdressAndId, INameAndDate
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Creating object:
IAdressAndId t = new TestClass()
{
Address = "address",
ID = 132,
Name = string.Empty,
DateTime = null
};
Also u can put your interfaces in separate namespace and make your class declaration as internal. After that create some public factories which will create the instances of your classes.

Check if List<T> element contains an item with a Particular Property Value

public class Item
{
public List<int> val { get; set; }
public double support { get; set; }
}
I declare variable:
List<Item> t = new List<Item>();
t.Add(new Item(){val = new List<int>(){1,2,3};support=.1);
var b = new Item();
b.val = t[0].val;
b.support=t[0].support;
t.Contain(b) // return false???
I'm try with linq
t.Any(a=>a.val==b.val) // I'm get error Expression cannot contain lambda expressions
3 possibilities come to mind:
You could implement IEquatable<T>:
public class Item: IEquatable<Item>
{
public List<int> val { get; set; }
public double support { get; set; }
public bool Equals(Item other)
{
return
this.support == other.support &&
this.val.SequenceEqual(other.val);
}
}
and now t.Contains(b) will return true.
If you cannot modify the Item class you could write a custom EqualityComparer:
public class ItemEqualityComparer : IEqualityComparer<Item>
{
private ItemEqualityComparer()
{
}
public static IEqualityComparer<Item> Instance
{
get
{
return new ItemEqualityComparer();
}
}
public bool Equals(Item x, Item y)
{
return
x.support == y.support &&
x.val.SequenceEqual(y.val);
}
public int GetHashCode(Item obj)
{
int hash = 27;
hash += (13 * hash) + obj.support.GetHashCode();
foreach (var item in obj.val)
{
hash += (13 * hash) + item.GetHashCode();
}
return hash;
}
}
and then t.Contains(b) will also return true.
Or if you prefer simply do it naively:
List<Item> t = new List<Item>();
t.Add(new Item { val = new List<int>(){1,2,3}, support=.1 });
var b = new Item();
b.val = t[0].val;
b.support = t[0].support;
bool equals = t.All(item => item.support == b.support && item.val.SequenceEqual(b.val));
Console.WriteLine(equals);
Your t.Any(a=>a.val == b.val) is correct.
The error you get is from the quick watch or expression window in the debugger, not from the compiler. Visual Studio's expression evaluator does not handle lambdas. However, it's still valid c# code, and will do what you want.
It's your earlier line that's a problem:
t.Add(new Item(){val = new List<int>(){1,2,3};support=.1);
This is a mixture of various different bits of syntax. It should be:
t.Add(new Item(){val = new List<int>(){1,2,3}, support=.1});
... although preferably with better property names, etc. Then the rest should work - although you need to do something with the result of Any. The Any call itself is valid. Here's a short but complete program which works:
using System;
using System.Collections.Generic;
using System.Linq;
public class Item
{
public List<int> Values { get; set; }
public double Support { get; set; }
}
class Test
{
static void Main()
{
List<Item> list = new List<Item>
{
new Item { Values = new List<int>{1, 2, 3},
Support = 0.1 }
};
var check = new Item { Values = list[0].Values,
Support = list[0].Support };
bool found = list.Any(a => a.Values == check.Values);
Console.WriteLine(found);
}
}
Note that this is performing a reference comparison between the two lists - if you created a different list with the same values (1, 2, 3), that wouldn't be found. You'd need to use a.Values.SequenceEqual(b.Values) or something similar.
Your Item class should implemenet the IEquatable interface:
class Item : IEquatable<Item>{
public List<int> val { get; set; }
public double support { get; set; }
public bool Equals(Item item){
return this.support.Equals(item.support) && this.val.SequenceEqual(item.val);
}
}
Then the Contains() method should work well.
You can correct your "t.Contain(b)" to t.Contains(b, new ItemEqualityComparer()) from System.Linq where ItemEqualityComparer will be your class, which will implement IEqualityComparer<Item>

Categories

Resources