Hooking up form events to different objects - c#

I'm writing an Windows Forms application that reads a file, processes it and then commits the changes.
Depending on some circumstances, when i click on "Process button" from the form i want it to interact with a specific object, for example depending on the extension of the file, the file gets processed by a specific class that handles that type of file.
In terms of application architecture, what's the best way to do this?

So for example a .txt file is processed by a TextParser, a .csv file is processed by a CSVParser, a .doc file is processed by a MSWordParser, everything else is processed by a GenericParser, etc.?
Well, the first thing would be to create an interface, i.e. IParser so that every class has a .Parse(file) method.
Next is the task of matching up the file extension strings with classes. The first thing that comes to mind for me is a Dictionary. The key would be the file extension as a string and the value would be a parser for that type of file extension. Before doing any processing you would populate the dictionary with all of the file extensions you wish to support.
Some assumptions for this to work well:
-You don't have a very large number of supported file extensions.
-One instance of the parser can be used repeatedly
-new-ing a parser isn't particularly expensive; there is minimal initialization beyond what the language needs to do to new an object.

I would go with something like a Factory pattern.
public interface IFileParser
{
void perse(string fileToParse);
}
public static class FileParserFactory
{
public static IFileParser GetParser(string fileToParse)
{
FileInfo file = new FileInfo(fileToParse);
IFileParser parserToReturn = null;
switch (file.Extension.ToLower())
{
case "csv": parserToReturn = new CSVFileParser(fileToParse);
break;
case "txt": parserToReturn = new TextFileParser(fileToParse);
break;
}
return parserToReturn;
}
}

Related

Inverted pipeline/chain of responsibility pattern

I am wondering whether there is an establish pattern to control the flow that my application will have.
Simply put, it's supposed to be something like that:
User provides a file
File is being processed
User receives a processed file
There will be several processing steps, lets say
PreprocessingOne, PreprocessingTwo, PreprocessingThree and FinalProcessing.
Naturally, we do not control the files that the user provides - they will require a different amount of preprocessing steps.
Since my message handler services will be in separate APIs, I don't want to invoke them just to return 'Cannot process yet' or 'Does not require processing' for performance reason.
Similarily, I don't want to pass the uploaded file around between services.
Ideally, I would like to design the flow for a file dynamically by evaluating the content and inserting only those of the message handlers that make sense.
I am saying 'Inverted' pipeline, because instead of going from A to Z I would rather like to check which stages I need starting from Z and only insert the last ones.
So, if the uploaded file qualifies for FinalProcessing right away, the flow would be just one element.
If the file requires to go from PreprocessingTwo then the flow would be PreprocessingTwo > PreprocessingThree > FinalProcessing
So, I was thinking I could implement something like that, but I am not sure about the details.
public interface IMessageHandler
{
void Process(IFile file);
}
public interface IContentEvaluator
{
IList<IMessageHandler> PrepareWorkflow(IFile file);
}
public interface IPipelineExecutor
{
void ExecuteWorkflow(IList<IMessageHandler> workflow, IFile file);
}
And then in the application
public void Start(IFile newFile)
{
var contentEvaluator = new ContentEvaluator(this.availableHandlers); // would be DI
var workflow = contentEvaluator.PrepareWorkflow(newFile);
this.executor.ExecuteWorkflow(workflow, newFile);
}
Could you please advise, recommend some approach or further read?
You can consider to use Strategy pattern: ...selects an algorithm at runtime...
But if you have too many combinations of the flow than the number of strategies which needs to be implemented will increase and solution can be complex.
Another approach can be to use SEDA: ...decomposes a complex, event-driven application into a set of stages connected by queues...
PreprocessingOne, PreprocessingTwo, PreprocessingThree and FinalProcessing are the stages, and flows can be defined by directing outgoing messages to different queues.
Is that a decorator pattern
Definition
Attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending
functionality.

Dynamic localization of messages

I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.
I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.

Library Message Handling

I am building a library which can parse complex files into a datamodel. Inside the library during parsing and during some other operations on the data structure I am building certain messages might occur (info, warnings, errors).
I need a way for the user of the library to fetch those messages. All stuff I am thinking about boils down to some static Event or a static list of those messages. But I want each object of the datastructure to have its own message queue.
An example:
class Program
{
static void Main(string[] args)
{
CalibData cd1 = new CalibData();
cd1.LoadFile(#"C:\tmp\file.ext");
var messageList = cd1.GetMessages();
cd1.DoOtherStuff();
CalibData cd2 = new CalibData();
cd2.LoadFile(#"C:\tmp\file2.ext");
cd2.LoadFile(#"C:\tmp\file3.ext2");
messageList = cd1.GetMessages(); //Do other stuff could have produced new Messages
var messageList2 = cd2.GetMessages();
}
}
Do you have any suggestions on how to implement such behavoir? I need something which is globally accessibly inside each instance, but each instance has another global messenger.
Additional Information:
Internall I am using an ANTLR Parser which generates a lot of objects (50.000+). Once the datastructure is created a lot of crossreferences are being set on the objects, etc. My main problem is, that I either have to create a static member to handle this, or from LoadFile() pass a messenger very deep into my function calls of the parser, cross referencing etc. In my opionion both is a rather bad choice. Changing the design is not an option since there is more to my problem. The datastructure is stored in 2 files (1 file = description, other file = data). So I can call something like
CalibData cd = new CalibData();
cd.LoadFile("description file"); //after this call the datastructure is built, but it hasn't got any value data yet
cd.LoadFile("data file") //now the structure also has value data
cd.ClearData();
cd.LoadFile("yet another data file"); //same structure different data
It looks like your LoadFile method currently doesn't return anything - why not make it return a data structure containing the errors, warnings etc? No need for anything global or static - or even persistent. Just the result of a method call.
In fact, I'd probably change this slightly so that a separate class (rather than the model itself) was responsible for loading, and the LoadFile call would return a result containing:
Information and errors
The resulting model object
That way any time you have a model you know it contains actual data - rather than it being "ready to load" as it were.

How to use XmlReader to read data in a List Part 2

Okay, it appeared that I had an answer for this question, but I was wrong. Probably because I asked the wrong thing. Here is what I want to do:
I want to take an xml document and pull it into memory. Then, at will, I want to run queries on that document and get information out of it. I want to do this whenever I need data WITHOUT loading the document into memory again. After all, the point is to stop hitting up the disk when I need the data. The methods I've been using only work ONCE, and I need to do this multiple times. Here is the code that I tried:
public static class GrabFile
{
public static XDocument configData = XDocument.Load(#"myxml.xml");
public static XmlReader templateReader = configData.CreateReader();
}
I thought this would copy the document into memory, and I'd be able to use the templateReader to query the configData any time I wanted to by using this:
while (GrabFile.templateReader.Reader())
{
//get the data I wanted
}
I thought I could put that while statement, and create others to do specific queries in a method, and call them whenever I needed the info. But it only works ONCE.
As I said, I'm not overly familiar with this, so there's probably a way to do it that's easy.
Also, people in the other thread wanted an example of what was in the xml document. That's irrelevant. The point is I want to put the document into memory, then query it as many times as needed with out accessing it from the disk and creating another reader. And yes, I want to use a reader for this.
Perhaps I need to move the pointer in the file in memory back to the top so it'll read it again?
Thanks for any help.
Why do you not want to create another reader?
Just calling GrabFile.configData.CreateReader() each time you need it is the simplest approach. (That won't load it from disk again, but it will create a separate XmlReader each time you call it.)
From the docs for XmlReader:
Represents a reader that provides fast, non-cached, forward-only access to XML data.
(Emphasis mine.) I don't see anything around resetting, and I wouldn't expect to.
Given that you've got the information in an XDocument to start with, I would personally try to do all the querying with that rather than using the rather-harder-to-work-with XmlReader anyway, but obviously that's your call. It would help if you'd give some justification though - as you say you're "not overly familiar with this", so it's worth revisiting your assumptions about how you're tackling whatever the higher-level task is.
Are you doing this in a server environment, like with an ASP.NET application? That might introduce some more steps, but the general idea is to use a static object to hold the XML Document that you load into memory. Much like you have done. And that's really all there is to it. Could you provide some more context?
You also don't really need to use the XML reader (unless you only want that sort of forward only access);
public static class Globals
{
public static XDocument ConfigFile = XDocument.Load("C:/somefile.xml");
}
public void SomeOtherFunctionSomewhere()
{
var configName = Globals.ConfigFile
.Descendants("someconfigsection")
.Descendants("configName")
.First().Value;
}
If you are storing your program's configuration in an XML file - consider reading the entire configuration into an in-memory object that you create yourself at startup. This will be an even faster way to access the config sections during runtime than having to query the XML every time (even if the XML is already in memory). Contrived example below:
public class Configuration
{
//just some made up config settings
public string Name {get;set;}
public int Id {get;set;}
public int XDimension {get;set;}
public int YDimension {get;set;}
}
class Program
{
static Configuration Config;
static void Main(string[] args)
{
Config = ReadConfig();
}
private static Configuration ReadConfig()
{
var config = new Configuration();
//read entire XML and set properties on the config object
return config;
}
}

Build C# dll from SQL table

I have a SQL-table with three columns: Id, English and Norwegian. Id is the primary key. In my application I have a flag (EN/NO) to decide which language to use for labels, buttons ++ in the GUI.
The application is now doing a select * everytime the application loads, and the application is looking up all required values at runtime. But instead of loading the whole dataset for every instance, i want to export these values and create a dll so i can store these values locally.
Is there any possibility of creating this in-code so the dll will renew itself with every build? Or do I have to run some external program to dynamically create ex. a .cs code to copy/paste into my class? (I need to be able to re-run the process because rows will be added every time there is a need for a new label/text)
I have so far thought out three solutions on how to structure my export, but no clue on how to export the data:
Preserve the state of the DataTable in a static context and provide help-methods to standardize the way of getting the values out.
Create a class containing each unique ID as method-name, and a parameter to decide which value to return:
public static class Names
{
public static string 12345(string language)
{
switch (language)
{
case "EN":
return "Hello";
case "NO":
return "Hei";
default:
return "Hello";
}
}
}
Create a class containing a searchable list for each language with ID as key and the value (as value)
Why don't you create different resource files for different languages and load the appropriate one depending you the settings. You can do this by using System.Resources.ResourceManager. This article here explains this in detail.
EDIT: Following SO post also discuss this in detail Best practice to make a multi language application in C#/WinForms?
No, i don't like the idea to put internationalization strings into a class library, Why you don't just use the .NET internationalization feature already built in in the framework ?
Resource files are the best solution, not class library for this kind of work ...

Categories