Note: I've read this and its not quite what I'm looking for:
I have an app that builds up XML from an input file and creates one of two outputs, depending upon the file chosen. It was a "quick n dirty" app to get round an immediate problem, but I know it's going to find further use and want to pre-empt this by refactoring.
At the moment I have a "builder" class that takes the input (in its ctor) and exposes a property which is the required XElement. However, many of the XElements are identical, except for the content, for both of my XML outputs. (Oh, please ignore the validation part which I'll refactor separately)
So I'm looking at a sensible way of DRYing my app:
At the moment I have something like this.
public FirstBuilder(string line, int lineNumber, bool output, string subjectType, string inquiryCode)
{
var split = Regex.Split(line, #"\|");
if (split.Count() != SPLIT_COUNT)
throw new Exception("This does not appear to be a valid First Type input file.");
_lineNumber = lineNumber;
_reportId = output ? TXT_REPORT_ID : XML_REPORT_ID;
_subjectType = subjectType;
_responseType = output ? TXT_RESPONSE_TYPE : XML_REPONSE_TYPE;
_inquiryCode = inquiryCode;
_product = split[0];
_number = split[1];
_amount = split[2];
_currency = split[3];
_name = split[4];
_nationalId = split[5];
_gender = split[6];
_dateOfBirth = split[7];
_nationality = split[8];
}
public XElement RequestElement
{
get
{
return new XElement("REQUEST",
new XAttribute("REQUEST_ID", _lineNumber),
RequestParametersElement,
SearchParametersElement);
}
}
private XElement RequestParametersElement
{
get
{
return new XElement("REQUEST_PARAMETERS",
ReportParametersElement,
InquiryPurposeElement,
ApplicationElement);
}
}
private XElement ReportParametersElement
{
get
{
return new XElement("REPORT_PARAMETERS",
new XAttribute("REPORT_ID", _reportId),
new XAttribute("SUBJECT_TYPE", _subjectType),
new XAttribute("RESPONSE_TYPE", _responseType));
}
}
etc. etc...
//used by
var x = new FirstBuilder(x,y,z,etc.).RequestElement();
This all works and is very fast...but SecondBuilder also uses these same elements, along with some different ones.
So I'm looking at the "best" way to refactor these out:
Shared Abstract class with inheritance?
Shared "Helper" classes?
Extension methods to return "built" elements?
Classes per element that extend XElement?
My suspicion is that this will balloon from the two examples as a quick solution to around 30 in then next month!
THX.
I would refactor the private properties to methods that receive the required values as input parameters. You can then move them into a PartsBuilder class and use that one in you concrete builders.
Related
I have code like this:
string FileName = "Users.json";
var DeserializeOptions = new JsonSerializerOptions { AllowTrailingCommas = true };
string jsonstring = File.ReadAllText(FileName);
var result = JsonSerializer.Deserialize<Root>(jsonstring, DeserializeOptions);
Root is list of 2 objects: Monsters and People. I use these 4 lines of code in my win forms program more than 10 times. Is it possible to change this code for class and use in other .cs files using shorter code?
Sure - put your code into a method:
internal static class MyUtilities
{
public static Root DoMyStuff()
{
string FileName = "Users.json";
var DeserializeOptions = new JsonSerializerOptions { AllowTrailingCommas = true };
string jsonstring = File.ReadAllText(FileName);
return JsonSerializer.Deserialize<Root>(jsonstring, DeserializeOptions);
}
}
Depending on your use case and the case your code varies across those different instances (not sure whether it is 100% the same, or uses different filenames, for example), you may want to accept some of those values as parameters.
In any case, you can then replace all of your code by single lines like this:
var result = MyUtilities.DoMyStuff();
My question is sort of like the one found here:
How do I name variables dynamically in C#?
However its a bit different so I'm wondering if its possible.
I'm trying to read in a bunch of strings from a .settings file.
I have them all named Time1, Time2,Time3 etc...
I want the User to be able to add more Times to the file so there could be Time100 or more.
I have a Size in the settings file that will keep track of the amount of Time Variables.
I want to write something that will read in all of the Time strings. I think it would be silly to pre-fill the .settings file with 100 Time Variables so I know they are there and then manually read in each one.
So I'm wondering if there is a way where I can read in Timei or Time+i where I is an integer that I can put in a loop that will find them all.
(note that the data is string not time, its just going to be converted to days of the week)
Such as: (Days is from ApplicationSettingsBase [aka file add new Settings1.settings]
public static int AvDaysIndex = Days.Default.Size; //This holds the number of items in Days
public static DayOfWeek[] AvailableDays = new DayOfWeek[AvDaysIndex]; //This is where I wants to read in all the variables Aka Time1 Time2 Times3
public ReadInDays() //Reads in from the .settings File
{
if(AvDaysIndex>0) // Makes sure there is something in Days to read
{
int I=0;
//I can Manually do the following
AvailableDays[I++] = Days.Default.Time1;
AvailableDays[I++] = Days.Default.Time2;
AvailableDays[I++] = Days.Default.Time3; //etc...
//Is there a way to do something like this
for (int i = 0; i < AvDaysIndex; i++) //reads in each time
{
AvailableDays[i] = Days.Default.Time +i;//where I would be added to the variable name to find it?
//Or something like
AvailableDays[i] = Days.Default.Time(I.tostring())
}
}
}
Hopefully all that at least makes it clear what I'm trying to do.
Edit - I'm starting to think my issue is actually with the .settings file. and that if I just read values in from another file type where the values don't have names I can easily read them in even though there is a variable number of elements in the file.
Solution -
for (int i = 0; i < Index; i++)
{
AvailableDays[i] = getFromFile(_Days.Default.Properties["Time" + (i+1).ToString()].DefaultValue.ToString());
AvailableTimes[i] = Convert.ToDateTime(_Times.Default.Properties["Time" + (i + 1).ToString()].DefaultValue);
}
It was all in figuring out how to read in from the .settings file and instead of reading it in directly aka Days.Default.Time1; I had to to do a generic lookup from Days.Default.Properties and then I could create a dynamic name and find it. You guys probably were trying to tell me how to do this, I just didn't understand.
Thanks again to all those that helped.
I would use a hashtable/dictionary to store the Days.Default.TimeX variations
hashtable["Time1"]...hashtable["TimeN"]
As already mentioned a hashtable or a dictionary would probably serve you best. If you go the dictionary route you can create a string/int indexer on the class and you would be able to alter your code slightly:
http://msdn.microsoft.com/en-us/library/2549tw02%28v=vs.80%29.aspx - Example of creating indexer on a class:
Example Indexer Class:
public class Default
{
private Dictionary<int, DayOfWeek> _values = new Dictionary<int,DayOfWeek>();
public DayOfWeek this[int index]
{
get
{
if (_values.ContainsKey(index))
return _values[index];
else
return null;
}
set
{
_values[index] = value;
}
}
}
Original:
AvailableDays[i] = Days.Default.Time(I.tostring())
Would become:
AvailableDays[i] = Days.Default.Time[I];
Reflection is always an option too and i have an example below that is in a Windows Console Application:
public class Default
{
public int Time1 { get; set; }
public int Time2 { get; set; }
public int Time3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
Default d = new Default();
Type t = d.GetType();
foreach (var info in t.GetProperties())
{
//SET VALUE
info.SetValue(d, 1);
}
foreach (var info in t.GetProperties())
{
//GET VALUE
Console.WriteLine("Property: {0}", info.Name);
Console.WriteLine("Value: {0}", info.GetValue(d));
}
//OR JUST ONE PROPERTY
Console.WriteLine("Time1 Property Value: {0}", t.GetProperty("Time1").GetValue(d));
Console.ReadLine();//PAUSE THE CONSOLE AFTER PROCESSING
}
}
In your example using reflection:
Days.Default.GetType().GetProperty("Time" + I.ToString()).GetValue(Days.Default) as DayOfWeek;
Another option could be to use Reflection. And getting the values from the enum on the fly.
See the link: How to Get Enum Values with Reflection in C#
However, using a Dictionary<string, DayOfWeek> will give you better performance and more readable code.
I think you could better resolve your problem by implementing your configuration using a
ConfigurationElementCollection. Then the 'names' of the configuration elements are irrelevant. You enumerate a collection of values and use those directly.
See here for an example; How to implement a ConfigurationSection with a ConfigurationElementCollection.
I have a static class where I keep a large number of relative paths that are used in different places in my application. It looks like that:
static class FilePathConstants
{
public const string FirstDirectory = "First";
public const string FirstSecondDirectory = "First/Second";
public const string FirstSecondThirdFileA = "First/Second/Third/FileA";
public const string FirstSecondFourthFileB = "First/Second/Fourth/FileB";
... nearly 100 of similar members
}
All of them are relative to some parent directory, location of which I know only during the program run. I need to keep them all together because it allows me to easily control what files are used by my application and change their locations from time to time.
However even though they are organized in alphabetic order and its easy to find a certain path, I need to be able to change some of them depending on some setting. Lets say, there is a setting 'bool SettingA' and when I turn it on, I have to do modify some of the paths to use a different directory or a different file name.
The problem is that now I can't use constants, I have to rewrite my code to properties or methods so that I can change file paths at runtime. And here where my code becomes much bigger in size and the strict order now looks ugly. Is there a way I can group them, so that it will not confuse anybody who uses this code? I can't break them into a separate classes because it is difficult to remember in what class what constant you may keep. For now I'm grouping them by regions, but I have a bad feeling that keeping more than one hundred of properties in one class is wrong.
Edit:
All directories and files that I declare in FilePathConstants are used in a large number of places in application (each path can be used multiple times, taking into account the fact that there is more then one hundred of paths - this is a large number). I would like to keep the interface of this class the same or with minimum changes to other classes that use them.
maybe you could use rowstructs
Use something like "index" file to store the directory paths and load it in runtime.
const string indexFilePath = #"C:\dirlist.txt";
IEnumerable<string> paths = File.ReadAllLines(indexFilePath);
Update
I would like to suggest using indirection - "mapper" class.
Here is how it should look like.
public enum FileSystemElement
{
FirstDirectory,
FirstSecondDirectory,
FirstSecondThirdFileA,
FirstSecondFourthFileB
}
public class FileSystemMapper
{
private readonly string _rootDirectory;
private readonly Dictionary<FileSystemElement, string> _fileElements;
public FileSystemMapper(string rootDirectory, string fileName)
{
_rootDirectory = rootDirectory;
string[] lines = File.ReadAllLines(fileName);
_fileElements = lines.Select(ParsePair).ToDictionary(pair => pair.Key, pair => pair.Value);
}
public string GetPath(FileSystemElement element)
{
string relativePath;
if (!_fileElements.TryGetValue(element, out relativePath))
{
throw new InvalidOperationException("Element not found");
}
string resultPath = Path.Combine(_rootDirectory, relativePath);
return resultPath;
}
private static KeyValuePair<FileSystemElement, string> ParsePair(string line)
{
const string separator = "|";
// File element alias | Path
if (string.IsNullOrEmpty(line))
throw new ArgumentException("Null or empty line", "line");
string[] components = line.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
if (components.Length != 2)
throw new ArgumentException("Line has invalid format", "line");
FileSystemElement element;
bool parseResult = FileSystemElement.TryParse(components[0], out element);
if (!parseResult)
throw new ArgumentException("Invalid element name", "line");
string path = components[1]; // for clarity
return new KeyValuePair<FileSystemElement, string>(element, path);
}
Client example:
FileSystemMapper fileSystemMapper = new FileSystemMapper(#"C:\root", #"C:\dirs.txt");
string firstDirectory = fileSystemMapper.GetPath(FileSystemElement.FirstDirectory);
string secondDirectory = fileSystemMapper.GetPath(FileSystemElement.FirstSecondDirectory);
string secondThirdFile = fileSystemMapper.GetPath(FileSystemElement.FirstSecondThirdFileA);
Index file format: <Element name>|<Path><New Line>
Example:
FirstDirectory|First
FirstSecondDirectory|First\Second
FirstSecondThirdFileA|First\Second\Third\FileA
FirstSecondFourthFileB|First\Second\Fourth\FileB
can you not use your projects Properties.Settings? it's stored in the .config file so can be edited after deployment
or just dont make them const, then you can edit them at runtime bu they revert to the original setting on next run.
or dont make the calss static and create an instance each time you use it, then change whats needed and discard the instance when finished.
I am trying to import a file with multiple record definition in it. Each one can also have a header record so I thought I would define a definition interface like so.
public interface IRecordDefinition<T>
{
bool Matches(string row);
T MapRow(string row);
bool AreRecordsNested { get; }
GenericLoadClass ToGenericLoad(T input);
}
I then created a concrete implementation for a class.
public class TestDefinition : IRecordDefinition<Test>
{
public bool Matches(string row)
{
return row.Split('\t')[0] == "1";
}
public Test MapColumns(string[] columns)
{
return new Test {val = columns[0].parseDate("ddmmYYYY")};
}
public bool AreRecordsNested
{
get { return true; }
}
public GenericLoadClass ToGenericLoad(Test input)
{
return new GenericLoadClass {Value = input.val};
}
}
However for each File Definition I need to store a list of the record definitions so I can then loop through each line in the file and process it accordingly.
Firstly am I on the right track
or is there a better way to do it?
I would split this process into two pieces.
First, a specific process to split the file with multiple types into multiple files. If the files are fixed width, I have had a lot of luck with regular expressions. For example, assume the following is a text file with three different record types.
TE20110223 A 1
RE20110223 BB 2
CE20110223 CCC 3
You can see there is a pattern here, hopefully the person who decided to put all the record types in the same file gave you a way to identify those types. In the case above you would define three regular expressions.
string pattern1 = #"^TE(?<DATE>[0-9]{8})(?<NEXT1>.{2})(?<NEXT2>.{2})";
string pattern2 = #"^RE(?<DATE>[0-9]{8})(?<NEXT1>.{3})(?<NEXT2>.{2})";
string pattern3 = #"^CE(?<DATE>[0-9]{8})(?<NEXT1>.{4})(?<NEXT2>.{2})";
Regex Regex1 = new Regex(pattern1);
Regex Regex2 = new Regex(pattern2);
Regex Regex3 = new Regex(pattern3);
StringBuilder FirstStringBuilder = new StringBuilder();
StringBuilder SecondStringBuilder = new StringBuilder();
StringBuilder ThirdStringBuilder = new StringBuilder();
string Line = "";
Match LineMatch;
FileInfo myFile = new FileInfo("yourFile.txt");
using (StreamReader s = new StreamReader(f.FullName))
{
while (s.Peek() != -1)
{
Line = s.ReadLine();
LineMatch = Regex1.Match(Line);
if (LineMatch.Success)
{
//Write this line to a new file
}
LineMatch = Regex2.Match(Line);
if (LineMatch.Success)
{
//Write this line to a new file
}
LineMatch = Regex3.Match(Line);
if (LineMatch.Success)
{
//Write this line to a new file
}
}
}
Next, take the split files and run them through a generic process, that you most likely already have, to import them. This works well because when the process inevitably fails, you can narrow it to the single record type that is failing and not impact all the record types. Archive the main text file along with the split files and your life will be much easier as well.
Dealing with these kinds of transmitted files is hard, because someone else controls them and you never know when they are going to change. Logging the original file as well as a receipt of the import is very import and shouldn't be overlooked either. You can make that as simple or as complex as you want, but I tend to write a receipt to a db and copy the primary key from that table into a foreign key in the table I have imported the data into, then never change that data. I like to keep a unmolested copy of the import on the file system as well as on the DB server because there are inevitable conversion / transformation issues that you will need to track down.
Hope this helps, because this is not a trivial task. I think you are on the right track, but instead of processing/importing each line separately...write them to a separate file. I am assuming this is financial data, which is one of the reasons I think provability at every step is important.
I think the FileHelpers library solves a number of your problems:
Strong types
Delimited
Fixed-width
Record-by-Record operations
I'm sure you could consolidate this into a type hierarchy that could tie in custom binary formats as well.
Have you looked at something using Linq? This is a quick example of Linq to Text and Linq to Csv.
I think it would be much simpler to use "yield return" and IEnumerable to get what you want working. This way you could probably get away with only having 1 method on your interface.
We have some stuff that may be exported into various formats. Currently we have these formats represented by an enum like this:
[Flags]
public enum ExportFormat
{
None = 0x0,
Csv = 0x1,
Tsv = 0x2,
Excel = 0x4,
All = Excel | Csv | Tsv
}
Problem is that these must be enumerated and they also need a translation or description in the ui. Currently I solved this by creating two extension methods. They work, but I don't really like them or the solution at all... they feel kind of smelly. Problem is I don't really know how I could do this better. Does anyone have any good alternatives? These are the two methods:
public static IEnumerable<ExportFormat> Formats(this ExportFormat exportFormats)
{
foreach (ExportFormat e in Enum.GetValues(typeof (ExportFormat)))
{
if (e == ExportFormat.None || e == ExportFormat.All)
continue;
if ((exportFormats & e) == e)
yield return e;
}
}
public static string Describe(this ExportFormat e)
{
var r = new List<string>();
if ((e & ExportFormat.Csv) == ExportFormat.Csv)
r.Add("Comma Separated Values");
if ((e & ExportFormat.Tsv) == ExportFormat.Tsv)
r.Add("Tab Separated Values");
if ((e & ExportFormat.Excel) == ExportFormat.Excel)
r.Add("Microsoft Excel 2007");
return r.Join(", ");
}
Maybe this is the way to do this, but I have a feeling there must be better ways to do it. How could I refactor this?
You could use the Formats method inside Describe to avoid doing all the bit operations at multiple places, like this:
private static Dictionary<ExportFormat, string> FormatDescriptions =
new Dictionary<ExportFormat,string>()
{
{ ExportFormat.Csv, "Comma Separated Values" },
{ ExportFormat.Tsv, "Tab Separated Values" },
{ ExportFormat.Excel, "Microsoft Excel 2007" },
};
public static string Describe(this ExportFormat e)
{
var formats = e.Formats();
var descriptions = formats.Select(fmt => FormatDescriptions[fmt]);
return string.Join(", ", descriptions.ToArray());
}
This way, it is easy to incorporate the string descriptions from an external source or localization, as hinted above.
The only other way comes to my mind is the usage of the System.Attribute class.
public class FormatDescription : Attribute
{
public string Description { get; private set; }
public FormatDescription(string description)
{
Description = description;
}
}
And then use Reflection with in your Describe function.
The only benefit of this method would be to have definition and the description at one place.
Dupe: How do I have an enum bound combobox with custom string formatting for enum values?
You could write an TypeConverter that reads specified attributes to look them up in your resources. Thus you would get multi-language support for display names without much hastle.
Look into the TypeConverter's ConvertFrom/ConvertTo methods, and use reflection to read attributes on your enum fields.
Addition:
Scroll down in the linked post for a implementation of a TypeConverter that does part of what is required for full support.
This would support an application where you have several languages at the same time, not only code name -> english name.
Remember that this is only the display name, never the stored value. You should always store the code name, or the integer value, to support users with different locales using the same data.