I'm currently studying C# and I'm quiet stunned over a simple task.
I have this code to test:
public interface IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { set; }
}
public static class MedicalScheduler
{
public static Dictionary<DateTime, string> Appointments { get; set; } = new Dictionary<DateTime, string>();
public static List<DateTime> FreeSlots { get; set; } = new List<DateTime>();
public static IEnumerable<Tuple<string, bool>> Schedule(IEnumerable<IAppointment> requests)
{
bool slotFound = false;
foreach (var appointment in requests)
{
if (slotFound) continue;
foreach (var times in appointment.ProposedTimes)
{
var freeSlot = FreeSlots.Where(s => s.Date == times.Date).FirstOrDefault();
if (freeSlot != null)
{
slotFound = true;
Appointments.Remove(freeSlot);
appointment.SelectedAppointmentTime = freeSlot;
yield return new Tuple<string, bool>(appointment.PatientName, true);
}
}
yield return new Tuple<string, bool>(appointment.PatientName, false);
}
}
}
And I'm required to test "Schedule" with a certain set of parameters. For example, I need to test it with empty Appointments and FreeList but with a single element in "requests".
I think I have understood how to compile a Unit Test and to set the Dictionary and List parameters. But I'm not sure how to create the IEnumerable variable.
My idea was to create a List of IAppointment(s), but how can I implement the interface in the test unit? I have tried using Moq but I didn't understood how I should use it correctly.
I'm sorry if the request seems quite confusing, but I don't know how to explain better :)
Thanks in advance for the help.
Please see the following example:
[Test]
public void Schedule()
{
// Arrange
var appointmentMock = new Mock<IAppointment>();
appointmentMock.Setup(appointment => appointment.PatientName).Returns("Dixie Dörner");
appointmentMock.Setup(appointment => appointment.ProposedTimes).Returns(
new List<DateTime>
{
new DateTime(1953,4,12),
new DateTime(1953,4,13)
});
var requests = new List<IAppointment>{appointmentMock.Object};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
MedicalScheduler.Schedule accepts any parameter implementing IEnumerable<IAppointment>, e. g. List<IAppointment> or Collection<IAppointment>.
So you simply create a List<IAppointment> and fill it with custom instances of IAppointment.
You can use Moq for creating the instances, as I did in the example. But for my own projects, I prefer the builder pattern:
internal static class AppointmentBuilder
{
public static IAppointment CreateDefault() => new Appointment();
public static IAppointment WithPatientName(this IAppointment appointment, string patientName)
{
appointment.PatientName = patientName;
return appointment;
}
public static IAppointment WithProposedTimes(this IAppointment appointment, params DateTime[] proposedTimes)
{
appointment.ProposedTimes = proposedTimes;
return appointment;
}
private class Appointment : IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { get; set; }
}
}
[Test]
public void Schedule()
{
// Arrange
var requests = new List<IAppointment>{AppointmentBuilder.CreateDefault()
.WithPatientName("Dixie")
.WithProposedTimes(new DateTime(1953,4,12))};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
Related
I have big model, that aggragates data for buisness entity.
class BigObject
{
TypeA DataA { get;set; }
TypeB DataB { get;set; }
TypeC DataC { get;set; }
}
and have service, which fill fields of model from differents sources. Some data depends from another data
class DataService
{
public BigObject GetModel()
{
var model = new BigObject();
model.DataA = sourceServiceA.GetData();
model.DataB = sourceServiceB.GetData(model.DataA.Id);
model.DataC = sourceServiceC.GetData();
}
}
In method GetModel() I need to configure, which fields should be filled, which should not. For example, I want to fill DataA property, but don't want fill others. First idea is pass in method object BigObjectFilter
public BigObject GetModel(BigObjectFilter filter)
class BigObjectFilter
{
bool FillDataA { get; set; }
bool FillDataB { get; set; }
bool FillDataC { get; set; }
}
and initialize this object in DataService clients.
In GetObject method I was going to add conditions like
if (filter.FillDataA)
{
model.DataA = sourceServiceA.GetData();
}
if (filter.FillDataC)
{
model.DataC = sourceServiceC.GetData();
}
I see, that this solution looks like bad practice. I would like to improve this construction. How can i improve it? I can't see, how to use builder pattern in this case, because i have requeired and optional data, one depends on the other.
For the sake of simplicity let's assume that TypeA, TypeB and TypeC are int?.
We can define a command class for the BigObject with the following constructors:
class BigObjectCommand
{
private readonly Func<BigObjectFilter, bool> canExecute;
private readonly Action<BigObject>? executeWithoutParam;
private readonly Action<int, BigObject>? executeWithParam;
private readonly Expression<Func<BigObject, int?>>? dependsOn;
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<BigObject> execute)
{
this.canExecute = canExecute;
this.executeWithoutParam = execute;
}
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<int, BigObject> execute, Expression<Func<BigObject, int?>> dependsOn)
{
this.canExecute = canExecute;
this.executeWithParam = execute;
this.dependsOn = dependsOn;
}
}
The first constructor will be used to cover the DataA and DataC properties' initialization
The second constructor will be used to cover the initialization of DataB property
Now, we can define an Evaluate method to implement the core logic
public void Evaluate(BigObjectFilter filter, BigObject context)
{
if (!canExecute(filter))
return; //or throw exception
if (executeWithoutParam is not null)
{
executeWithoutParam(context);
return;
}
var input = dependsOn!.Compile()(context);
if (!input.HasValue)
return; //or throw exception
executeWithParam!(input.Value, context);
}
If the condition fails we don't do the assignment
If the assignment does not require any input then we simply execute it
If the assignment depends on an input then we check whether it is populated or not and depending on the result we may or may not execute the assignment
With these in our hand the GetModel can be implemented like this:
private readonly List<BigObjectCommand> Commands;
public DataService()
{
Commands = new()
{
new (filter => filter.FillDataA, (m) => m.DataA = sourceServiceA.GetData()),
new (filter => filter.FillDataB, (i, m) => m.DataB = sourceServiceB.GetData(i), m => m.DataA),
new (filter => filter.FillDataC, (m) => m.DataC = sourceServiceC.GetData()),
};
}
public BigObject GetModel(BigObjectFilter filter)
{
var model = new BigObject();
foreach (var command in Commands)
{
command.Evaluate(filter, model);
}
return model;
}
This solution is far from perfect I just wanted to share with you the basic idea how to apply the Command pattern for your problem.
Here you can a find a working example. By changing the FillDataXYZ values you can see how the Evaluate works in practice.
It looks like you have at least two choices here:
use some collection which stores value to handle
an approach inspired by Chain-of-responsibility pattern
Let's start from collection which stores value to handle
At first, we need our class with properties to be filled:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
Then this is our class which will handle all your properties:
public class BigObjectHandler
{
Dictionary<string, Action> _handlerByproperty = new ();
BigObject _bigObject;
public BigObjectHandler(BigObject bigObject)
{
_bigObject = bigObject;
_handlerByproperty.Add("A", GetDataA);
_handlerByproperty.Add("B", GetDataB);
_handlerByproperty.Add("C", GetDataC);
}
public void Handle(string propertyName) =>
_handlerByproperty[propertyName].Invoke();
private void GetDataA()
{
_bigObject.A = 1; // sourceServiceA.GetData();
}
private void GetDataB()
{
_bigObject.B = 1; // sourceServiceA.GetData();
}
private void GetDataC()
{
_bigObject.C = 1; // sourceServiceA.GetData();
}
}
And then you can call the above code like this:
IEnumerable<string> propertiesToFill = new List<string> { "A", "B" };
BigObject bigObject = new ();
BigObjectHandler bigObjectMapHandler = new (bigObject);
foreach (var propertyToFill in propertiesToFill)
{
bigObjectMapHandler.Handle(propertyToFill);
}
OUTPUT:
A = 1
B = 1
Chain-of-responsibility pattern
If you have many if else statements, then you can try to use "Chain-of-responsibility pattern". As wiki says:
the chain-of-responsibility pattern is a behavioral design pattern
consisting of a source of command objects and a series of processing
objects. Each processing object contains logic that defines the
types of command objects that it can handle; the rest are passed to
the next processing object in the chain. A mechanism also exists for
adding new processing objects to the end of this chain
However, we will not stop execution if some of condition is met. Let me show an example.
At first, we need some abstraction of handler:
public abstract class BigObjectHandler
{
private BigObjectHandler _nextBigObjectHandler;
public void SetSuccessor(BigObjectHandler bigObjectHandler)
{
_nextBigObjectHandler = bigObjectHandler;
}
public virtual BigObject Execute(BigObject bigObject,
BigObjectFilter parameter)
{
if (_nextBigObjectHandler != null)
return _nextBigObjectHandler.Execute(bigObject, parameter);
return bigObject;
}
}
Then we need concrete implemenatation of these handlers for your properties. This properties will be filled
by your sourceServiceX.GetData():
public class BigObjectAHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillA)
{
bigObject.A = 1; // sourceServiceA.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectBHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillB)
{
bigObject.B = 2; // sourceServiceB.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectCHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillC)
{
bigObject.C = 3; // sourceServiceC.GetData();
}
return base.Execute(bigObject, filter);
}
}
And these are object with data:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
And some filter which will contain settings of what property should be filled:
public class BigObjectFilter
{
public bool FillA { get; set; } = true;
public bool FillB { get; set; }
public bool FillC { get; set; }
}
And then we can call the above code like this:
BigObjectHandler chain = new BigObjectAHandler();
BigObjectHandler objectBHandler = new BigObjectBHandler();
BigObjectHandler objectCHandler = new BigObjectCHandler();
chain.SetSuccessor(objectBHandler);
objectBHandler.SetSuccessor(objectCHandler);
BigObjectFilter bigObjectFilter = new BigObjectFilter();
bigObjectFilter.FillA = true;
BigObject vehicle = chain.Execute(new BigObject(), bigObjectFilter); // A = 1
It can be seen after code execution that onle property A is handled. Output is:
A = 1
B = 1
I tried to write unit test for getMark() and faced problem with Moq, with which I'm not familiar. I have no idea what method and object properly mock in order to unit test getMark()
Here is my MarkServiceClass containing getMark()
public class MarkService : IMarkService
{
IMarkService _markService;
IStdService _stdService;
IStdService _stdMService;
RClass cs;
public MarkService(IMarkService markService, IStdService stdService, IStdService stdMService)
{
_markService = markService;
_stdService = stdService;
_stdMService = stdMService;
}
public bool Init(int sID, string pID, string year)
{
try
{
cs = new RClass ();
cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.UID == pID);
var mInfo = __stdMService.GetSTDM(sID, pID, year);
cs.Type = mInfo.CalculateAmount;
return true;
}
catch
{
return false;
}
}
public MarkVM getMark(int sID, string pID, string year)
{
var output=Init(sID, pID, year);
if (!output)
return null;
int sGrade= 0;
int sMark= 0;
//here are conditions where sGrade and sMark used
return new MarkVM
{
Grade = sGrade,
Mark = sMark
};
}
}
and MarkVM
public class MarkVM
{
public int Grade { get; set; }
public int Mark { get; set; }
}
The code you shared is not complete so I had to make some assumptions to give you an example how to unit test getMark
public class MarkVM
{
public int Grade { get; set; }
public int Mark { get; set; }
}
Not knowing what RClass is, I define it with minimal requirements
public class RClass {
public String Uid { get; set; }
public string sLevel { get; set; }
public int Type { get; set; }
}
Same for this Info your service retrieves with GetSTDM
public class Info
{
public int CalculateAmount { get; set; }
}
Now come the interfaces. This is definitely required if you want to mock
public interface IStdService
{
List<RClass> GetAsIQueryable();
Info GetSTDM(int sID, string pID, string year);
}
Those 2 methods are the ones you'll want to mock if you unit test getMark.
Mocking getMark itself will only allow you to check it is called, but not its behavior which is the purpose of unit testing.
Now the main class. I removed the injection of IMarkService in the constructor because I really don't see why you would do that: Markservice implements IMarkService here.
For any reason you use 2 instances of IStdService, I kelpt that but then you need to inject it too.
public class MarkService : IMarkService
{
private IStdService __stdService;
private IStdService __stdMService;
public RClass cs;
public MarkService(IStdService stdMService, IStdService stdService)
{
__stdMService = stdMService;
__stdService = stdService;
}
public bool Init(int sID, string pID, string year)
{
try
{
cs = new RClass();
cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.Uid == pID).sLevel;
var mInfo = __stdMService.GetSTDM(sID, pID, year);
cs.Type = mInfo.CalculateAmount;
return true;
}
catch
{
return false;
}
}
public MarkVM getMark(int sID, string pID, string year)
{
var output = Init(sID, pID, year);
if (!output)
return null;
int sGrade = 0;
int sMark = 0;
//here are conditions where sGrade and sMark used
return new MarkVM
{
Grade = sGrade,
Mark = sMark
};
}
}
Now comes the test. If you want to unit test getMark you could either mock Init from IMarkService, or consider the behavior comes from this Init and then you want to mock GetAsIQueryable and GetSTDM.
I made the assumption second option is what you want.
using System.Collections.Generic;
using MarkServiceNS;
using Moq;// Moq framework where you'll find everything you need
using NUnit.Framework;// Using NUnit for unit test. Because I like it :-)
using NUnit.Framework.Constraints;
namespace UnitTestWithMoqExample
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void getMark()
{
var mockedStdService = new Mock<IStdService>();
mockedStdService.Setup(x => x.GetAsIQueryable())
.Returns(new List<RClass> { new RClass { Uid = "uid", sLevel = "expected", Type = 1 } }); // Here you define what it the mocked result of GetAsIQueryable call.
var mockedStdMService = new Mock<IStdService>();
mockedStdMService.Setup(x => x.GetSTDM(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new Info { CalculateAmount = 1 });// Same here. You mock GetSTDM. The method parameters are not expected to change the behavior in my unit test, this is why I consider It.Any<T> so whatever you pass to the mock, the result will be the same.
// Here is the assertion. This should do the job
var service = new MarkServiceNS.MarkService(mockedStdMService.Object, mockedStdService.Object);
Assert.IsNotNull(service.getMark(1, "", ""));
Assert.IsInstanceOf(typeof(MarkVM), service.getMark(1, "", ""));
Assert.AreEqual(0, service.getMark(1, "", "").Grade);
Assert.AreEqual(0, service.getMark(1, "", "").Mark);
}
}
}
A basic Moq coding will be like this
[Test]
public void Test1()
{
var mock = new Mock<IMarkService>();
mock.Setup(p => p.getMark(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>())).Returns(new MarkVM());
mock.Verify(p => p.getMark(1001, "P001", "2022"), Times.Once());
}
I am posting this as an example as I don't have your full code
Use the above technique to moq your methods GetAsIQueryable and GetSTDM and CalculateAmount
And call the method Init and then call the getMark
I have a class that contains Range[] as property and Range class is a self referencing class. I used [JsonIgnore] to prevent StackoverflowException but it works for only Serialize not Deserialize. How can I fix this?
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace testoverflow
{
class Program
{
public static void Main(string[] args)
{
GlobalVariable.Json = "[{\"TotalBytesReceived\":0,\"Id\":\"b03750fb291a46708f8e1a7409553075\",\"NofThread\":8,\"Speed\":0,\"Progress\":0.0,\"FilePath\":\"C:\\\\Users\\\\kafeinaltor\\\\Downloads\",\"RangeDir\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\",\"Url\":\"http://ipv4.download.thinkbroadband.com/20MB.zip\",\"Ranges\":[{\"Start\":0,\"End\":9223372036854775806,\"TotalBytesReceived\":0,\"IsDownloaded\":false,\"FileId\":\"87cd7715dc0740c1b82ddd681bf2523d\",\"Size\":9223372036854775807,\"Status\":4,\"IsIdle\":false,\"SaveDir\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\",\"FilePath\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\\\\87cd7715dc0740c1b82ddd681bf2523d\",\"Md5Checksum\":null}],\"Info\":null,\"DownloadRequestMessage\":null}]";
var a = new MTDO();
Console.WriteLine(GlobalVariable.Json);
Console.ReadKey(true);
}
public static class GlobalVariable
{
public static string Json { get; set; }
}
public class MTDO
{
public MTDO()
{
Ranges = new Range[]
{
new Range(0L, 100L, ""),
new Range(101L, 200L, "")
};
Id = Guid.NewGuid().ToString("N");
Reminder.AddOrUpdate(this);
}
public string Id { get; set; }
public Range[] Ranges{ get; set; }
}
public class Range
{
public long Start { get; set; }
public long End { get; set; }
public string SaveDir { get; set; }
public long TotalBytesReceived{ get; set; }
public Range(long start, long end, string saveDir)
{
this.Start = start;
this.End = end;
this.SaveDir = Guid.NewGuid().ToString();
}
[JsonIgnore]
public Range Remaining
{
get
{
return new Range(Start + TotalBytesReceived, End, SaveDir);
}
}
}
public class Reminder
{
public Reminder()
{
}
public static void AddOrUpdate(MTDO mtdo)
{
var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
if (list == null)
list = new List<MTDO>();
var exists = list.Any(x => x.Id == mtdo.Id);
if (!exists)
list.Add(mtdo);
else
{
var i = list.Select((x, j) => new {val = x, index = j})
.First(x => x.val.Id == mtdo.Id).index;
list[i] = mtdo;
}
WriteJson(list);
}
public static List<MTDO> ReadList()
{
var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
if (list == null)
list = new List<MTDO>();
return list;
}
static string Read()
{
try
{
return GlobalVariable.Json;
}
catch
{
return "";
}
}
static void WriteJson(List<MTDO> list)
{
GlobalVariable.Json = JsonConvert.SerializeObject(list);
}
}
}
}
UPDATE: I have updated myquestion adding minimum reproducable code in Console Application. You can copy/paste and run directly.
The problem is that you have an infinite recursion:
You call MTDO constructor
Inside MTDO constructor you call Reminder.AddOrUpdate(this);
Inside that method you have var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
Which calls MTDO constructor again (step 1)
These steps keep repeating until you get StackOverflowException.
I have a multilingual database, which returns values based on a key and an enum Language. When I convert a DB object to a model, I want the model to contain the translated value based on the key and the current language.
The key comes from the DB object but how can I pass the current language to the the Mapper.Map() function?
Currently, I am using a [ThreadStatic] attribute to set the culture before calling Mapper.Map<>, and to retrieve it in the TypeConverter.
public enum Language
{
English, French, Italian, Maltese
}
public class MultilingualValue<T>
{
public Dictionary<Language, T> Value { get; set; }
public MultilingualValue()
{
this.Value = new Dictionary<Language, T>();
}
}
public class PersonData
{
public string FirstName { get; set; }
public MultilingualValue<string> City { get; set; }
}
public void MapPerson()
{
PersonData personData = new PersonData();
personData.FirstName = "John";
personData.City = new MultilingualValue<string>();
personData.City.Value[ Language.English] = "The Zurrieq";
personData.City.Value[Language.French] = "Le Zurrieque";
MultilingualValueData.CurrentLanguage = Language.English;
var personModel = Mapper.Map<PersonData, PersonModel>(personData);
}
public class MultilingualValueToBasicDataTypeConverter<T> : ITypeConverter<MultilingualValue<T>, T>
{
public T Convert(ResolutionContext context)
{
var currentLanguage = MultilingualValueData.CurrentLanguage; //THIS IS THE [ThreadStatic] VARIABLE
if (currentLanguage == null) throw new InvalidOperationException("Please make sure to fill in CurrentLanguage");
MultilingualValue<T> sourceMultilingualValue = (MultilingualValue < T > )context.SourceValue;
T destinationValue = default(T);
if (sourceMultilingualValue != null)
{
destinationValue = sourceMultilingualValue.Value[currentLanguage.Value];
}
return destinationValue;
}
}
public static class MultilingualValueData
{
[ThreadStatic]
public static Language? CurrentLanguage;
}
I left out the configurations as I think they're unneccessary for this example. If you need them, I'll post them as well.
While this works, I find this workaround quite ugly. Is there any way to pass data through the ResolutionContext?
Just use the Map overload that takes a Action<IMappingOperationOptions>. You can add configuration elements to the Items property that are then passed to your ITypeConverter
public class CustomConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
return "translated in " + context.Options.Items["language"];
}
}
internal class Program
{
private static void Main(string[] args)
{
AutoMapper.Mapper.CreateMap<string, string>().ConvertUsing<CustomConverter>();
var result = AutoMapper.Mapper.Map<string, string>("value" , opt => opt.Items["language"] = "english");
Console.Write(result); // prints "translated in english"
Console.ReadLine();
}
}
,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.