Avoiding repetition when writing different objects with identical data structures - c#

In my current system I've got two EF objects with near-identical sets of properties but which can't for various reasons, be unified or joined to share some of the same structure. It's effectively personal details, so it looks a bit like this:
public partial class Person
{
public int PersonID { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
// .. etc
}
public partial class Member
{
public int MemberID { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
// .. etc
}
Eventually a mixture of these two object types have to be written to the same file, like this:
foreach (Person p in People)
{
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.Append("\"");
lineBuilder.Append(p.PersonID.ToString());
lineBuilder.Append("\",\"");
lineBuilder.Append(p.Title);
lineBuilder.Append("\",\"");
lineBuilder.Append(p.Forename);
lineBuilder.Append("\",\"");
// etc...
}
foreach (Member m in Members)
{
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.Append("\"");
lineBuilder.Append(m.MemberID.ToString());
lineBuilder.Append("\",\"");
lineBuilder.Append(m.Title);
lineBuilder.Append("\",\"");
lineBuilder.Append(m.Forename);
lineBuilder.Append("\",\"");
// etc...
}
This is obviously A Bad Thing - not only does it repeat code, but it's reliant on both loops maintaining the same number of columns in the output which might easily be overlooked if the code is changed.
Short of creating an intermediary object and mapping the fields on to it - which is really just shifting the problem elsewhere - is there a way to approach this that allows me to get this down to a single loop?

Put an interface on the two entities and let your logic work against the interface instead of the concrete classes

Assuming you are stuck with those two entities since you are using DB first EF, three suggestions I can think of:
Use AutoMapper to map both these objects to a third class. In the third class overwrite ToString(). If you include the AutoMapper unit test to validate mappings you never have to worry about changes - AutoMapper will pick it up and fail the test.
Refactor the DB to a polymorphic Person table with a Person type field and then you have just one Person entity with a person type field.
Use code first EF and have a base person class, or interface.

You're doing two things here. You're wrapping fields in quotes, and you're joining a series of strings together with commas. Simply write a method that does that for any number of arbitrary strings:
public static string BuildLine(IEnumerable<string> fields)
{
return string.Join(",", fields.Select(WrapInQuotes));
}
private static string WrapInQuotes(string rawData)
{
return string.Format("\"{0}\"", rawData);
}
Once you have this you can now have each of your classes simply provide a collection of their fields, without being responsible for formatting them for output.
Note that it looks like you're writing out a CSV file here. My first advice is don't. Just use an existing CSV writer that will be able to handle all of the character escaping and so on for you so that you don't have to. There are some things that you haven't accounted for so far including fields that have line breaks or quotes in them, or the fact that you really shouldn't be quote escaping every single field, but rather only those that contain characters requiring quote escaping (namely line breaks, if you want to support multi-line fields, or commas).

Create an interface (or abstract class) for it.
public interface IDetails
{
string Title;
string Surname;
string AddressLine1;
string AddressLine2;
}
public partial class Member : IDetails
{
//etc
}
public partial class Person : IDetails
{
//etc
}

you can use interface,
public interface ISocialHuman{
public int PersonID { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
public static void writeHumanSomewhere(ISocialHuman){
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.Append("\"");
lineBuilder.Append(p.PersonID);
lineBuilder.Append("\",\"");
lineBuilder.Append(p.Title);
lineBuilder.Append("\",\"");
lineBuilder.Append(p.Forename);
lineBuilder.Append("\",\"");
}
But you should change the ID name.

Related

C# Optimal way to get property from all class members of same type

I'm trying to shorten the code to most efficient way to access a specific property in all the members of the class.
public class Title
{
public string reference { get; set; }
public string root { get; set; }
public string name { get; set; }
public string description { get; set; }
public string shortT { get; set; }
}
public class Results
{
public List<Title> pop { get; set; }
public List<Title> exact { get; set; }
public List<Title> sub { get; set; }
...
}
Results is coming out of a JSON deserialize. It contains members of the same type, pop, exact and sub (the exact number of members could vary). I would like to filter all the objects in Results who's name contains "jackie"
So what would be the optimal/efficient way to iterate through the Lists of the members (pop, exact, sub) and look at the property name and see if it contains "jackie", without having to directly reference pop, exact or sub.
Reason being that the JSON could return more members tomorrow like pop or exact etc that I don't know in advanced, so I need to find a way to combine them all into a single searchable list (as they are all of the same type).
Consequently is a way to directly reference the property name for all the members in a single statement as in
foreach (string n in Results*.name)
you can make List of that classes and access with expression
List<Title> asdf = new List<Title>;
asdf.where(var => var.name == "jackie").ToList().ForEach(var =>
{
//do actions with this classes var.
};
Hope I help you ;)
You'll have to reference all three properties if you want to access what's in them. If you want to view the contents of all three as one collection you can Union them.
// given that r is an instance of Results
var combinedTitles = r.exact.Union(r.pop).Union(r.sub);
var matches = combinedTitles .Where(title => title.name == "title you want to match");

Facade a class without writing lots of boilerplate code?

Let's say I have a class from a 3rd-party, which is a data-model. It has perhaps 100 properties (some with public setters and getters, others with public getters but private setters). Let's call this class ContosoEmployeeModel
I want to facade this class with an interface (INavigationItem, which has Name and DBID properties) to allow it to be used in my application (it's a PowerShell provider, but that's not important right now). However, it also needs to be usable as a ContosoEmployeeModel.
My initial implementation looked like this:
public class ContosoEmployeeModel
{
// Note this class is not under my control. I'm supplied
// an instance of it that I have to work with.
public DateTime EmployeeDateOfBirth { get; set; }
// and 99 other properties.
}
public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
private ContosoEmployeeModel model;
public FacadedEmployeeModel(ContosoEmployeeModel model)
{
this.model = model;
}
// INavigationItem properties
string INavigationItem.Name { get; set;}
int INavigationItem.DBID { get; set;}
// ContosoEmployeeModel properties
public DateTime EmployeeDateOfBirth
{
get { return this.model.EmployeeDateOfBirth; }
set { this.model.EmployeeDateOfBirth = value; }
}
// And now write 99 more properties that look like this :-(
}
However, it's clear that this will involve writing a huge amount of boilerplate code to expose all the properties , and I'd rather avoid this if I can. I can T4 code-generate this code in a partial class, and will do if there aren't any better ideas, but I though I'd ask here to see if anyone had any better ideas using some super wizzy bit of C# magic
Please note - the API I use to obtain the ContosoEmployeeModel can only return a ContosoEmployeeModel - I can't extend it to return a FacededEmployeeModel, so wrapping the model is the only solution I can think of - I'm happy to be corrected though :)
The other approach may be suitable for you is to use AutoMapper to map base class to your facade here is sample code:
class Program
{
static void Main(string[] args)
{
var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };
Mapper.CreateMap<Model, FacadeForModel>();
var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);
Console.WriteLine(mappedObject);
Console.ReadLine();
}
class Model
{
public string Name { get; set; }
public DateTime Date { get; set; }
public int Count { get; set; }
}
interface INavigationItem
{
int Id { get; set; }
string OtherProp { get; set; }
}
class FacadeForModel : Model, INavigationItem
{
public int Id { get; set; }
public string OtherProp { get; set; }
}
}
Resharper allows the creation of "delegating members", which copies the interface of a contained object onto the containing object and tunnels the method calls/property access through to the contained object.
http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html
Once you've done that, you can then extract an interface on your proxy class.

Cloning A Class

I have two classes which contain the same fields, however one inherits some properties from somewhere else and the other does not.
I have created a generic list using the class "ZEUS_ResearchStocksHistory" , but then I need to clone all of the fields over to the other list "ZEUS_ResearchStocksHistoryWithExcel". I don't want to have to loop through each field in one list and populate the other, or write some sort of linq join, there must be a faster way?
The reason I can't use the same class in both instances is that when inheriting the ExcelReport function it adds additional fields which I do not want when I display this list in a data grid.
internal class ZEUS_ResearchStocksHistory
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
internal class ZEUS_ResearchStocksHistoryWithExcel : ExcelReport
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
Is this possible?
Thanks
Did you have a look at automapper?
example from codeproject:
CustomerViewItem customerViewItem =
Mapper.Map<Customer, CustomerViewItem>(customer);
Check out Automapper, which is designed to do exactly this. Automapper is up on NuGet.
http://lostechies.com/jimmybogard/2009/01/23/automapper-the-object-object-mapper/
You could do something as simple as:
Mapper.CreateMap<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>();
var newObject = Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(oldObject);
Or, since you said you have a list, you could do:
var newList = oldList.Select(x => Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(x));

How to differentiate class properties using user-defined tags (custom-attributes)

Background:
I have a custom class, which represents a Data Base Table, each property corresponding to a table column. The properties can be classified in three ways.
Example: Take for example a Person object.
MetaProperties: (Columns that are needed by the program)
Person_ID: used in table for indexing etc...
UserDefinedType: (UDT), complex class handling write-permission on the table.
Timestamp: needed to handle the UDT in C# DataTables
RealProperties: (actual traits that describe the real Person)
FullName
DateOfBirth
PlaceOfBirth
EyeColor
etc... (many more)
RawDataProperties: (these columns hold raw data from external sources)
Phys_EyeColor: the eye-color, as directly imported from the physical traits database, may be in unknown format, may have conflicting value with entry from other db, or any other data quality issue...
HR_FullName: full name as given in HR file
Web_FullName: full name as taken from a web form
Web_EyeColor: eye color as taken from web form
etc...
public class Person
{
#region MetaProperties
public int Person_ID { get; set; }
public UserDefinedType UDT { get; set; }
public DateTime timestamp { get; set; }
#endregion
#region RealProperties
public string FullName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public Color EyeColor { get; set; }
//...
#endregion
#region RawDataProperties
public string Phys_EyeColor { get; set; }
public string Phys_BodyHeight { get; set; }
public string Web_FullName { get; set; }
public string Web_EyeColor { get; set; }
public string HR_FullName { get; set; }
//...
#endregion
}
Question: How can I programmatically differentiate between these three types of properties in my Person class? The goal is to be able to iterate through properties of a certain type using System.Reflection or some other organisational construct. Pseudocode:
foreach(Property prop in Person.GetPropertiesOfType("RealProperty"){
... doSmth(prop);
}
I'm thinking about writing custom Attributes, and hanging them on to the properties, sort of like taggin.
But since I know nothing about custom Attributes, I would like to ask if I'm going down the proper path, or if there are any other better ways of doing this.
Note: the shown example may may not be the best in terms of program design, and I am well aware that inheritance or splitting up the class otherwise could solve this problem. But that is not my question - I want to know if properties in a class can be tagged or somehow differentiated between using custom categories.
You can do this with custom attributes.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PropertyAttribute : System.Attribute
{
public PropertyType Type { get; private set; }
public PropertyAttribute (PropertyType type) { Type = type; }
}
public enum PropertyType
{
Meta,
Real,
Raw,
}
Then, you can do this with each property or field:
[PropertyType(PropertyType.Meta)]
public int Person_ID;
[PropertyType(PropertyType.Real)]
public string FullName;
[PropertyType(PropertyType.Raw)]
public string Phys_EyeColor;
Then you can access it with something like
foreach (PropertyAttribute attr in this.GetType().GetCustomAttributes(typeof(PropertyAttribute), false))
{
// Do something based on attr.Type
}

Enterprise Library Validation Block - Validate conditioned to another property value

I need to validate two fields only if a third field has a specific value.
In this code snipper i suppose to use a CheckIf properties that not exist.
It is possible to validate a field only if another property hase a specifica value ?
public string CustomerType { get; set; } // P=Private B=Business
[NotNullValidator(MessageTemplate = "You must specify the property 'Name'", CheckIf = "CustomerType=='P'")]
public string PrivateName { get; set; }
[NotNullValidator(MessageTemplate = "You must specify the property 'Name'", CheckIf = "CustomerType=='B'")]
public string BusinessName { get; set; }
Thank you!!!
From a validation perspective I agree with Siva that you can use SelfValidation for this. When looking at your code however, from an OO perspective, I can't help noticing that it might be good to take a good look at your design. It seems that either you are showing us two sub types of Customer, namely PrivateCustomer and BusinessCustomer:
class Customer
{
}
class PrivateCustomer : Customer
{
public string PrivateName { get; set; }
}
class BusinessCustomer : Customer
{
public string BusinessName { get; set; }
}
Or... those two properties are actually the same thing. Your validation messages even calls them 'Name' in both cases. In that case, you'll end up with this design:
class Customer : Customer
{
public string CustomerType { get; set; }
public string Name { get; set; }
}

Categories

Resources