Core .NET Recipes

Exclusive offer: get 50% off this eBook here
.Net Framework 4.5 Expert Programming Cookbook

.Net Framework 4.5 Expert Programming Cookbook — Save 50%

Over 50 engaging recipes for learning advanced concepts of .NET Framework 4.5 with this book and ebook.

$29.99    $15.00
by A P Rajshekhar | March 2013 | .NET Cookbooks Enterprise Articles Microsoft

This article by A.P. Rajshekhar, author of .Net Framework 4.5 Expert Programming Cookbook , will cover recipes related to core concepts in .NET, which will include the following:

  • Metadata-driven programming: The first six recipes will cover how to use attributes as metadata for specific purposes such as validation and localization.

  • Reflection: The Processing custom attributes via reflection recipe will tell you how to use reflection to create metadata processors such as applications or libraries that can understand custom attributes and provide the output based on them.

  • Asynchronous file I/O: This is a new feature for file input/output introduced in .NET 4.5. The Using asynchronous file I/O for directory-to-directory copy recipe will cover this feature.

  • Dynamic programming: .NET 4.0 introduced the concept of dynamic programming, in which blocks of code marked as dynamic will be executed directly, bypassing the compilation phase. We will look at this in the last recipe, Accessing JSON using dynamic programming.

 

(For more resources related to this topic, see here.)

Implementing the validation logic using the Repository pattern

The Repository pattern abstracts out data-based validation logic. It is a common misconception that to implement the Repository pattern you require a relational database such as MS SQL Server as the backend. Any collection can be treated as a backend for a Repository pattern. The only point to keep in mind is that the business logic or validation logic must treat it as a database for saving, retrieving, and validating its data. In this recipe, we will see how to use a generic collection as backend and abstract out the validation logic for the same.

The validation logic makes use of an entity that represents the data related to the user and a class that acts as the repository for the data allowing certain operations. In this case, the operation will include checking whether the user ID chosen by the user is unique or not.

How to do it...

The following steps will help check the uniqueness of a user ID that is chosen by the user:

  1. Launch Visual Studio .NET 2012. Create a new project of Class Library project type. Name it CookBook.Recipes.Core.CustomValidation.

  2. Add a folder to the project and set the folder name to DataModel.

  3. Add a new class and name it User.cs.

  4. Open the User class and create the following public properties:

    Property name

    Data type

    UserName

    String

    DateOfBirth

    DateTime

    Password

    String

    Use the automatic property functionality of .NET to create the properties. The final code of the User class will be as follows:

    namespace CookBook.Recipes.Core.CustomValidation { /// <summary> /// Contains details of the user being registered /// </summary> public class User { public string UserName { get; set; } public DateTime DateOfBirth { get; set; } public string Password { get; set; } } }

  5. Next, let us create the repository. Add a new folder and name it Repository.

  6. Add an interface to the Repository folder and name it IRepository.cs.

  7. The interface will be similar to the following code snippet:

    public interface IRepository { }

  8. Open the IRepository interface and add the following methods:

    Name

    Description

    Parameter(s)

    Return Type

    AddUser

    Adds a new user

    User object

    Void

    IsUsernameUnique

    Determines whether the username is already taken or not

    string

    Boolean

    After adding the methods, IRepository will look like the following code:

    namespace CookBook.Recipes.Core.CustomValidation { public interface IRepository { void AddUser(User user); bool IsUsernameUnique(string userName); } }

  9. Next, let us implement IRepository. Create a new class in the Repository folder and name it MockRepository.

  10. Make the MockRepository class implement IRepository. The code will be as follows:

    namespace CookBook.Recipes.Core.Data.Repository { public class MockRepository:IRepository { #region IRepository Members /// <summary> /// Adds a new user to the collection /// </summary> /// <param name="user"></param> public void AddUser(User user) { } /// <summary> /// Checks whether a user with the username already ///exists /// </summary> /// <param name="userName"></param> /// <returns></returns> public bool IsUsernameUnique(string userName) { } #endregion } }

  11. Now, add a private variable of type List<Users> in the MockRepository class. Name it _users. It will hold the registered users. It is a static variable so that it can hold usernames across multiple instantiations.

  12. Add a constructor to the class. Then initialize the _users list and add two users to the list:

    public class MockRepository:IRepository { #region Variables Private static List<User> _users; #endregion public MockRepository() { _users = new List<User>(); _users.Add(new User() { UserName = "wayne27", DateOfBirth = new DateTime(1950, 9, 27), Password = "knight" }); DateOfBirth = new DateTime(1955, 9, 27), Password = "justice" }); } #region IRepository Members /// <summary> /// Adds a new user to the collection /// </summary> /// <param name="user"></param> public void AddUser(User user) { } /// <summary> /// Checks whether a user with the username already exists /// </summary> /// <param name="userName"></param> /// <returns></returns> public bool IsUsernameUnique(string userName) { } #endregion }

  13. Now let us add the code to check whether the username is unique. Add the following statements to the IsUsernameUnique method:

    bool exists = _users.Exists(u=>u.UserName==userName); return !exists;

    The method turns out to be as follows:

    public bool IsUsernameUnique(string userName) { bool exists = _users.Exists(u=>u.UserName==userName); return !exists; }

  14. Modify the AddUser method so that it looks as follows:

    public void AddUser(User user) { _users.Add(user); }

How it works...

The core of the validation logic lies in the IsUsernameUnique method of the MockRespository class. The reason to place the logic in a different class rather than in the attribute itself was to decouple the attribute from the logic to be validated. It is also an attempt to make it future-proof. In other words, tomorrow, if we want to test the username against a list generated from an XML file, we don't have to modify the attribute. We will just change how IsUsernameUnique works and it will be reflected in the attribute. Also, creating a Plain Old CLR Object (POCO) to hold values entered by the user stops the validation logic code from directly accessing the source of input, that is, the Windows application.

Coming back to the IsUsernameUnique method, it makes use of the predicate feature provided by .NET. Predicate allows us to loop over a collection and find a particular item. Predicate can be a static function, a delegate, or a lambda. In our case it is a lambda.

bool exists = _users.Exists(u=>u.UserName==userName);

In the previous statement, .NET loops over _users and passes the current item to u. We then make use of the item held by u to check whether its username is equal to the username entered by the user. The Exists method returns true if the username is already present. However, we want to know whether the username is unique. So we flip the value returned by Exists in the return statement, as follows:

return !exists;

Creating a custom validation attribute by extending the validation data annotation

.NET provides data annotations as a part of the System.ComponentModel. DataAnnotation namespace. Data annotations are a set of attributes that provides out of the box validation, among other things. However, sometimes none of the in-built validations will suit your specific requirements. In such a scenario, you will have to create your own validation attribute. This recipe will tell you how to do that by extending the validation attribute. The attribute developed will check whether the supplied username is unique or not. We will make use of the validation logic implemented in the previous recipe to create a custom validation attribute named UniqueUserValidator.

How to do it...

The following steps will help you create a custom validation attribute to meet your specific requirements:

  1. Launch Visual Studio 2012. Open the CustomValidation solution.

  2. Add a reference to System.ComponentModel.DataAnnotations.

  3. Add a new class to the project and name it UniqueUserValidator.

  4. Add the following using statements:

    using System.ComponentModel.DataAnnotations; using CookBook.Recipes.Core.CustomValidation.MessageRepository; using CookBook.Recipes.Core.Data.Repository;

  5. Derive it from ValidationAttribute, which is a part of the System. ComponentModel.DataAnnotations namespace. In code, it would be as follows:

    namespace CookBook.Recipes.Core.CustomValidation { public class UniqueUserValidator:ValidationAttribute { } }

  6. Add a property of type IRepository to the class and name it Repository.

  7. Add a constructor and initialize Repository to an instance of MockRepository. Once the code is added, the class will be as follows:

    namespace CookBook.Recipes.Core.CustomValidation { public class UniqueUserValidator:ValidationAttribute { public IRepository Repository {get;set;} public UniqueUserValidator() { this.Repository = new MockRepository(); } } }

  8. Override the IsValid method of ValidationAttribute. Convert the object argument to string.

  9. Then call the IsUsernameUnique method of IRepository, pass the string value as a parameter, and return the result. After the modification, the code will be as follows:

    namespace CookBook.Recipes.Core.CustomValidation { public class UniqueUserValidator:ValidationAttribute { public IRepository Repository {get;set;} public UniqueUserValidator() { this.Repository = new MockRepository(); } public override bool IsValid(object value) { string valueToTest = Convert.ToString(value); return this.Repository.IsUsernameUnique(valueToTest); } } }

    We have completed the implementation of our custom validation attribute. Now let's test it out.

  10. Add a new Windows Forms Application project to the solution and name it CustomValidationApp.

  11. Add a reference to the System.ComponentModel.DataAnnotations and CustomValidation projects.

  12. Rename Form1.cs to Register.cs.

  13. Open Register.cs in the design mode. Add controls for username, date of birth, and password and also add two buttons to the form. The form should look like the following screenshot:

  14. Name the input control and button as given in the following table:

    Control

    Name

    Textbox

    txtUsername

    Button

    btnOK

    Since we are validating the User Name field, our main concern is with the textbox for the username and the OK button. I have left out the names of other controls for brevity.

  15. Switch to the code view mode. In the constructor, add event handlers for the Click event of btnOK as shown in the following code:

    public Register() { InitializeComponent(); this.btnOK.Click += new EventHandler(btnOK_Click); } void btnOK_Click(object sender, EventArgs e) { }

  16. Open the User class of the CookBook.Recipes.Core.CustomValidation project. Annotate the UserName property with UniqueUserValidator. After modification, the User class will be as follows:

    namespace CookBook.Recipes.Core.CustomValidation { /// <summary> /// Contains details of the user being registered /// </summary> public class User { [UniqueUserValidator(ErrorMessage="User name already exists")] public string UserName { get; set; } public DateTime DateOfBirth { get; set; } public string Password { get; set; } } }

  17. Go back to Register.cs in the code view mode.

  18. Add the following using statements:

    using System.ComponentModel; using System.ComponentModel.DataAnnotations; using CookBook.Recipes.Core.CustomValidation; using CookBook.Recipes.Core.Data.Repository;

  19. Add the following code to the event handler of btnOK:

    //create a new user User user = new User() { UserName = txtUsername.Text, DateOfBirth=dtpDob.Value }; //create a validation context for the user instance ValidationContext context = new ValidationContext(user, null, null); //holds the validation errors IList<ValidationResult> errors = new List<ValidationResult>(); if (!Validator.TryValidateObject(user, context, errors, true)) { foreach (ValidationResult result in errors) MessageBox.Show(result.ErrorMessage); } else { IRepository repository = new MockRepository(); repository.AddUser(user); MessageBox.Show("New user added"); }

  20. Hit F5 to run the application. In the textbox add a username, say, dreamwatcher. Click on OK. You will get a message box stating User has been added successfully

  21. Enter the same username again and hit the OK button. This time you will get a message saying User name already exists. This means our attribute is working as desired.

  22. Go to File | Save Solution As…, enter CustomValidation for Name, and click on OK.

    We will be making use of this solution in the next recipe.

How it works...

To understand how UniqueUserValidator works, we have to understand how it is implemented and how it is used/called. Let's start with how it is implemented. It extends ValidationAttribute. The ValidationAttribute class is the base class for all the validation-related attributes provided by data annotations. So the declaration is as follows:

public class UniqueUserValidator:ValidationAttribute

This allowed us to make use of the public and protected methods/attribute arguments of ValidationAttribute as if it is a part of our attribute. Next, we have a property of type IRepository, which gets initialized to an instance of MockRepository. We have used the interface-based approach so that the attribute will only need a minor change if we decide to test the username against a database table or list generated from a file. In such a scenario, we will just change the following statement:

this.Repository = new MockRepository();

The previous statement will be changed to something such as the following:

this.Repository = new DBRepository();

Next, we overrode the IsValid method. This is the method that gets called when we use UniqueUserValidator. The parameter of the IsValid method is an object. So we have to typecast it to string and call the IsUniqueUsername method of the Repository property. That is what the following statements accomplish:

string valueToTest = Convert.ToString(value); return this.Repository.IsUsernameUnique(valueToTest);

Now let us see how we used the validator. We did it by decorating the UserName property of the User class:

[UniqueUserValidator(ErrorMessage="User name already exists")] public string UserName {get; set;}

As I already mentioned, deriving from ValidatorAttribute helps us in using its properties as well. That's why we can use ErrorMessage even if we have not implemented it.

Next, we have to tell .NET to use the attribute to validate the username that has been set. That is done by the following statements in the OK button's Click handler in the Register class:

ValidationContext context = new ValidationContext(user, null, null); //holds the validation errors IList<ValidationResult> errors = new List<ValidationResult>(); if (!Validator.TryValidateObject(user, context, errors, true))

First, we instantiate an object of ValidationContext. Its main purpose is to set up the context in which validation will be performed. In our case the context is the User object. Next, we call the TryValidateObject method of the Validator class with the User object, the ValidationContext object, and a list to hold the errors. We also tell the method that we need to validate all properties of the User object by setting the last argument to true. That's how we invoke the validation routine provided by .NET.

Using XML to generate a localized validation message

In the last recipe you saw that we can pass error messages to be displayed to the validation attribute. However, by default, the attributes accept only a message in the English language. To display a localized custom message, it needs to be fetched from an external source such as an XML file or database. In this recipe, we will see how to use an XML file to act as a backend for localized messages.

How to do it...

The following steps will help you generate a localized validation message using XML:

  1. Open CustomValidation.sln in Visual Studio .NET 2012.

  2. Add an XML file to the CookBook.Recipes.Core.CustomValidation project. Name it Messages.xml. In the Properties window, set Build Action to Embedded Resource.

  3. Add the following to the Messages.xml file:

    <?xml version="1.0" encoding="utf-8" ?> <messages> <en> <message key="not_unique_user">User name is not unique</message> </en> <fr> <message key="not_unique_user">Nom d'utilisateur n'est pas unique</message> </fr> </messages>

  4. Add a folder to the CookBook.Recipes.Core.CustomValidation project. Name it MessageRepository.

  5. Add an interface to the MessageRepository folder and name it IMessageRepository.

  6. Add a method to the interface and name it GetMessages. It will have IDictionary<string,string> as a return type and will accept a string value as parameter. The interface will look like the following code:

    namespace CookBook.Recipes.Core.CustomValidation.MessageRepository { public interface IMessageRepository { IDictionary<string, string> GetMessages(string locale); } }

  7. Add a class to the MessageRespository folder. Name it

    XmlMessageRepository

  8. Add the following using statements:

    using System.Xml;

  9. Implement the IMessageRepository interface. The class will look like the following code once we implement the interface:

    namespace CookBook.Recipes.Core.CustomValidation.MessageRepository { public class XmlMessageRepository:IMessageRepository { #region IMessageRepository Members public IDictionary<string, string> GetMessages(string locale) { return null; } #endregion } }

  10. Modify GetMessages so that it looks like the following code:

    ublic IDictionary<string, string> GetMessages(string locale) { XmlDocument xDoc = new XmlDocument(); xDoc.Load(Assembly.GetExecutingAssembly().GetManifestResourceS tream("CustomValidation.Messages.xml")); XmlNodeList resources = xDoc.SelectNodes("messages/"+locale+"/ message"); SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>(); foreach (XmlNode node in resources) { dictionary.Add(node.Attributes["key"].Value, node. InnerText); } return dictionary; }

Next let us see how to modify UniqueUserValidator so that it can localize the error message.

How it works...

The Messages.xml file and the GetMessages method of XmlMessageRespository form the core of the logic to generate a locale-specific message. Message.xml contains the key to the message within the locale tag. We have created the locale tag using the two-letter ISO name of a locale. So, for English it is <en></en> and for French it is <fr></fr>.

Each locale tag contains a message tag. The key attribute of the tag will have the key that will tell us which message tag contains the error message. So our code will be as follows:

<message key="not_unique_user">User name is not unique</message>

not_unique_user is the key to the User is not unique error message. In the GetMessages method, we first load the XML file. Since the file has been set as an embedded resource, we read it as a resource. To do so, we first got the executing assembly, that is, CustomValidation. Then we called GetManifestResourceAsStream and passed the qualified name of the resource, which in this case is CustomValidation.Messages.xml. That is what we achieved in the following statement:

xDoc.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream( "CustomValidation.Messages.xml"));

Then we constructed an XPath to the message tag using the locale passed as the parameter. Using the XPath query/expression we got the following message nodes:

XmlNodeList resources = xDoc.SelectNodes("messages/"+locale+"/ message");

After getting the node list, we looped over it to construct a dictionary. The value of the key attribute of the node became the key of the dictionary. And the value of the node became the corresponding value in the dictionary, as is evident from the following code:

SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>(); foreach (XmlNode node in resources) { dictionary.Add(node.Attributes["key"].Value, node.InnerText); }

The dictionary was then returned by the method. Next, let's understand how this dictionary is used by UniqueUserValidator.

.Net Framework 4.5 Expert Programming Cookbook Over 50 engaging recipes for learning advanced concepts of .NET Framework 4.5 with this book and ebook.
Published: January 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Extending the validation attribute for localization

If you do not want to hardcode the message or embed it as a resource, the only approach left is to extend the attribute so that messages can be decided at runtime based on the locale used. Extending an attribute for this purpose can also help if you decide to fetch the message from the database or an external translation service.

In this recipe, we will modify the UniqueUserValidator code so that it can generate locale-based custom messages. The custom messages will be fetched from an XML file using the logic developed in the previous recipe

How to do it...

The following steps will guide you as you generate locale-based custom messages:

  1. Open CustomValidation.sln in Visual Studio .NET 2012.

  2. Open UniqueUserValidator and add the following using statements:

    using CookBook.Recipes.Core.CustomValidation.MessageRepository; using CookBook.Recipes.Core.Data.Repository;

  3. Then add a property of type IMessageRepository and instantiate it in the constructor:

    public IRepository Repository {get;set;} public IMessageRepository MessageRepo {get;set;} public UniqueUserValidator() { this.Repository = new MockRepository(); this.MessageRepo = new XmlMessageRepository(); }

  4. Override the FormatErrorMessage method of ValidationAttribute. In the overridden method, get the current locale and call the GetMessage method of IMessageRepository with the locale value. Then, return the value corresponding to the ErrorMessage property. In code, it will be as follows:

    public override string FormatErrorMessage(string name) { string locale = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName; return this.MessageRepo.GetMessages(locale)[this.ErrorMessage]; }

    We have completed the modifications to UniqueUserValidator. Now let's see how to use it. Along with using the modified UniqueUserValidator code, we will also test whether it responds to the change in locale correctly.

  5. Open the User class, which is in the DataModel folder of the CookBook.Recipes. Core.CustomValidation project.

  6. Change the ErrorMessage parameter of the UserName property to the following:

    [UniqueUserValidator(ErrorMessage = "not_unique_user")] public string UserName { get; set; }

  7. Next, open the Register class of CustomValidationApp in the design mode. Add a label and a combobox. Name the combobox cmbLocale. After adding the controls, the Register form will look as follows:

  8. Next, open the Register class in the view code mode. Add the following code in the constructor:

    cmbLocale.Items.Add("en-IN"); cmbLocale.Items.Add("fr-FR"); cmbLocale.SelectedIndex = 0;

  9. Add an event handler for the SelectedIndexChanged event of cmbLocale as follows:

    public Register() { InitializeComponent(); cmbLocale.Items.Add("en-IN"); cmbLocale.Items.Add("fr-FR"); cmbLocale.SelectedIndex = 0; cmbLocale.SelectedIndexChanged += new EventHandler(cmbLocale_SelectedIndexChanged); this.btnCancel.Click += new EventHandler(btnCancel_Click); this.btnOK.Click += new EventHandler(btnOK_Click); } void cmbLocale_SelectedIndexChanged(object sender, EventArgs e) { }

  10. Add the following code to the event handler for the SelectedIndexChanged event of cmbLocale:

    Thread.CurrentThread.CurrentCulture = new CultureInfo(cmbLocale. SelectedItem.ToString());

  11. Press F5 to run the application. Enter wayne27 as username. Click on OK. You will get a message saying User name is not unique.

  12. Select fr-FR from the locale combobox. Click on the OK button. You will get a message saying Nom d'utilisateur n'est pas unique, which is the French version of the message we have used in Messages.xml.

How it works...

The main change we did to the attribute is overriding the FormatErrorMessage method of ValidationAttribute. The validation framework / data annotation library calls the FormatErrorMessage method when it needs to output a message corresponding to a property. In short, by overriding it, we can provide a customized message.

To do so, we first need to find the two-letter ISO name of the current locale. Using the CurrentCulture property of CurrentThread, which is a static property of the Thread class, we can find the locale name. The following code did that and provided us with the two-letter ISO name of the current locale:

string locale = Thread.CurrentThread.CurrentCulture. TwoLetterISOLanguageName;

Next, we passed the locale to the GetMessages method of IMessageRepository. From the returned dictionary, we found the message we wanted using the ErrorMessage property/named parameter and returned it. The value in ErrorMessage acted as the key:

return this.MessageRepo.GetMessages(locale)[this.ErrorMessage];

CustomValidationApp performs two roles. First of all, it makes use of the UserName property of the User class decorated with UniqueUserValidator to pass the key of the message we want, as shown:

[UniqueUserValidator(ErrorMessage = "not_unique_user")] public string UserName { get; set; }

The value we passed to ErrorMessage acted as the key in FormatErrorMessage. The other role the application performs is to provide us with a test platform for testing locales, in this case, English and French. This was done by the following code in the SelectedIndexChanged event handler of cmbLocale in the Register class:

Thread.CurrentThread.CurrentCulture = new CultureInfo(cmbLocale. SelectedItem.ToString());

What we did in the preceding code is to set the current culture of the application to the culture selected in the locale combobox. When we set the CurrentCulture property of CurrentThred of the Thread class to a particular culture, the culture of the application is changed until the application is closed. Use this only for testing purposes.

Creating custom attributes

In the previous recipes we saw how to extend existing attributes to suit our needs. However, there are situations where you don't have an existing attribute to extend. In such cases, you will have to create your own attribute. In this recipe we will look at creating custom attributes. Our attribute will help you to keep track of bugs fixed within a class. It can be used to tag the class itself or methods within the class.

A custom attribute is a class extending from System.Attribute. However, its behavior is quite different from a class. And to make it to work as an attribute, extra steps such as creating another class that can process the attribute are required.

How to do it...

  1. Launch Visual Studio .NET 2012. Create a project of type Class Library and name it CookBook.Recipes.Core.DefectTracker.

  2. Delete Class1.class from the project.

  3. Add a folder to the project and name it Attributes.

  4. Next, add a class to the folder and name it DefectTrackerAttribute.

  5. Derive the DefectTrackerAttribute class from Attribute.

  6. Add the following properties as shown in the following table:

    Name

    Type

    DefectID

    Int

    ResolvedBy

    String

    ResolvedBy

    String

    Comments

    String

    Once the properties are added, our class will look as follows:

    namespace CookBook.Recipes.Core.DefectTracker.Attributes { public class DefectTrackerAttribute:Attribute { #region Public Properties public int DefectID {get;set;} public string ResolvedBy {get;set;} public string ResolvedOn {get;set;} public string Comments {get;set;} #endregion } }

  7. Now, let's specify all the places where, within a class, we can use this attribute by decorating/tagging the class with the AttributeUsage attribute:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DefectTrackerAttribute:Attribute { #region Public Properties public int DefectID {get;set;} public string ResolvedBy {get;set;} public string ResolvedOn {get;set;} public string Comments {get;set;} #endregion } }

  8. Next, add a constructor so that we can pass the values via the constructor. The Comments parameter will be optional. In code, the constructor will be as follows:

    public DefectTrackerAttribute(int defectID, string resolvedBy, string resolvedOn, string Comments = "") { this.DefectID = defectID; this.ResolvedBy = resolvedBy; this.ResolvedOn = resolvedOn; this.Comments = Comments; }

    That completes the steps in creating DefectTrackerAttribute. Let's see how to use it.

  9. Add a project of type Class Library and name it DefectTrackerTest.

  10. Delete Class1.cs.

  11. Add a reference to the CookBook.Recipes.Core.DefectTracker project.

  12. Next, add a class to DefectTrackerTest and name it CurrencyConverter.

  13. Add the following import:

    using CookBook.Recipes.Core.DefectTracker.Attributes;

  14. Add a private variable of type double:

    private double _value;

  15. Add a parameterized constructor that will look as follows:

    [DefectTrackerAttribute(1042,"AP", "2012/02/11","Changed float param to double")] public CurrencyConverter(double value) { _value = value; }

  16. Add DefectTrackerAttribute to the constructor.

  17. Add a method that accepts a double argument and returns a double value.

  18. Tag the method with DefectTrackerAttribute. The method will look as follows:

    [DefectTrackerAttribute(DefectID = 1042, ResolvedBy = "AP", ResolvedOn = "2012/02/11")] public double ToRupee() { return _value * 50; }

How it works...

As I mentioned earlier, a custom attribute is really a class that is inherited from System.Attribute. Our DefectTrackerAttribute class is no different. So, we inherit it from Attribute:

public class DefectTrackerAttribute:Attribute { }

Now, we have to pass information to the attribute. This can be done in two ways:

  • Through a constructor

  • As a named parameter of the constructor

Using a constructor to pass the parameter is similar to what we do for classes. However, for the constructor parameters to be used as named parameters, we will need properties. So we added properties and the constructor:

public class DefectTrackerAttribute:Attribute { #region Public Properties public int DefectID {get;set;} public string ResolvedBy {get;set;} public string ResolvedOn {get;set;} public string Comments {get;set;} #endregion public DefectTrackerAttribute() { } public DefectTrackerAttribute(int defectID, string resolvedBy, string resolvedOn, string Comments = "") { this.DefectID = defectID; this.ResolvedBy = resolvedBy; this.ResolvedOn = resolvedOn; this.Comments = Comments; } }

The members of the class to which we can apply the attribute is the most important aspect of that attribute. To specify this, we can use the AttributeUsage attribute. Since we want to apply our attribute to classes, constructors, methods, fields, and properties, we specify it as follows:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DefectTrackerAttribute:Attribute { #region Public Properties public int DefectID {get;set;} public string ResolvedBy {get;set;} public string ResolvedOn {get;set;} public string Comments {get;set;} #endregion public DefectTrackerAttribute(int defectID, string resolvedBy, string resolvedOn, string Comments = "") { this.DefectID = defectID; this.ResolvedBy = resolvedBy; this.ResolvedOn = resolvedOn; this.Comments = Comments; } }

The AllowMultiple argument specifies whether the attribute can be used more than once on a class or its members. As the same member may be modified multiple times for different defects, we will be using our attribute multiple times on that member. Hence, we passed AllowMultiple as true. With that we come to the end of this recipe.

In DefectTrackerTest, we have just one class, CurrencyConverter. We tagged its constructor and ToRupee with DefectTrackerAttribute:

public CurrencyConverter(double value) { _value = value; } [DefectTrackerAttribute(DefectID = 1042, ResolvedBy = "AP", ResolvedOn = "2012/02/11")]public double ToRupee() { return _value * 50; }

In the next recipe we will see how to create a processor for our attribute and how to use them both.

Processing custom attributes via reflection

In the previous recipe, we developed a custom attribute named DefectTrackerAttribute. However, the attribute, by itself, does not do anything. Unless there is an application or library that looks at the class and members tagged/decorated by the attribute, it is just a piece of code that does nothing. So, in this recipe, we will see how to process the class tagged by DefectTrackerAttribute using reflection.

How to do it...

  1. Launch Visual Studio .NET 2012.

  2. Open CustomAttribute.sln.

  3. Open the CookBook.Recipes.Core.DefectTracker project in Add a folder to the project. Name it Processor.

  4. Add a class to the folder and name it DefectTrackerProcessor.

  5. Add the following imports:

    using System.Reflection; using CookBook.Recipes.Core.DefectTracker.Attributes;

  6. Add the following methods to it:

    Name

    Parameters

    Return type

    GetDetails

    String assmblyPath

    String className

    String

    GetMemberDetails

    String memberName

    IEnumerable<DefectTrackerAttribute> attributes

    String

  7. To the GetMemberDetails method, add the following code:

    StringBuilder sb = new StringBuilder(); sb.Append("\n"); if (!sb.ToString().Contains(memberName)) { sb.Append(memberName); } foreach (var attribute in attributes) { sb.Append("ID-"); sb.Append(attribute.DefectID); sb.Append("\t"); sb.Append("Resolved By-"); sb.Append(attribute.ResolvedBy); sb.Append("\t"); sb.Append("Resolved On-"); sb.Append(attribute.ResolvedOn); } return sb.ToString();

  8. To the GetDetails method, add the following code:

    StringBuilder details = new StringBuilder(); Assembly assembly = Assembly.LoadFrom(assemblyPath); Type type = assembly.GetType(className, true, true); //check whether the constructors have the custom attribute ConstructorInfo[] constructorInfo = type.GetConstructors(); foreach (var item in constructorInfo) { IEnumerable<DefectTrackerAttribute> attributes = item.GetCustomAttributes<DefectTrackerAttribute>(); details.Append(GetMemberDetails(item.Name, attributes)); } //check whether the methods have custom attribute MethodInfo[] methodInfo = type.GetMethods(); foreach (var item in methodInfo) { IEnumerable<DefectTrackerAttribute> attributes = item.GetCustomAttributes<DefectTrackerAttribute>(); if (attributes.Count() > 0) { details.Append(GetMemberDetails(item.Name, attributes)); } } return details.ToString();

    That completes the first step. Next, let us look at how to use the processor.

  9. Add a project of type Windows Forms Application and name it DefectTrackerApp.

  10. Add a reference to the CookBook.Recipes.Core.DefectTracker project.

  11. Rename the Form class to TrackDefect.

  12. Switch to the design mode. Design the form so that it looks like the following screenshot:

  13. Name the textboxes and buttons as follows:

    Control

    Description

    Name

    Textbox

    For the assembly path, that is, the path of the .dll or .exe file to be loaded

    txtAssembly

    Button

    To display the file open dialog

    btnOpen

    Textbox

    To enter the fully qualified class name to be loaded from the assembly

    txtClassName

    Button

    To call DefectTrackerProcessor

    btnLoad

    Textbox

    To display the details of the fixed defects in the class

    txtDetails

  14. Double-click on btnOpen to generate the Click event handler. In the event handler, add the following code:

    if( diagOpen.ShowDialog() == System.Windows.Forms.DialogResult.OK) txtPath.Text = diagOpen.FileName;

  15. Switch to the design mode. Double-click on btnOpen to generate the Click event handler. In the event handler, add the following code:

    if (!String.IsNullOrEmpty(txtPath.Text) && !String. IsNullOrEmpty(txtClassName.Text)) { txtDetails.Text = new DefectTrackerProcessor(). GetDetails(txtPath.Text, txtClassName.Text); }

  16. Add the following import:

    using CookBook.Recipes.Core.DefectTracker.Attributes;

  17. Set DefectTrackerApp as a startup project.

  18. Press F5 and run the application.

  19. Click on the Open button. In the file dialog, navigate to the bin folder of the DefectTrackerTest project. Select DefectTracker.dll. Observe the full path being displayed in the textbox next to the Open button.

  20. In the textbox next to the Load button, enter DefectTrackerTest. CurrencyConverter. Click on Load.

  21. The multiline textbox next to the Details label will be filled with details of the constructor and the methods that make use of the attribute.

That completes the steps for creating a processor for the custom attribute and using it. Next, let us dive more into the code to understand what is happening.

How it works...

The core of the DefectTrackerProcessor class is the GetDetails method that uses reflection to find out whether the class whose name has been sent as the parameter contains DefectTrackerAttribute. The first step is to load the assembly containing the class using the LoadFrom static method of the Assembly class. The next step is to retrieve the class from the assembly and set it to the Type class variable. These two steps are achieved in the following statements:

Assembly assembly = Assembly.LoadFrom(assemblyPath); Type type = assembly.GetType(className, true, true);

The Type class is the root of the functionality provided by .NET for reflection. It is the entry point to gain details regarding the members of a class or structure. In the preceding code, the GetType method looks into the assembly and returns the Type instance containing details of the class whose name has been passed via the className variable. The second parameter of GetType is set to true so that, if the class is not found, an exception is thrown and we can know that something is wrong. We want to find the class regardless of whether the class name is passed in upper- or lowercase. Hence, the last parameter is set to true to tell GetType to ignore the case of the class name.

Now that we have the Type instance containing the details of the class, we can check whether the members of the class are decorated with DefectTrackerAttribute or not. The first class member that we check is the constructor. The following statement provides the details of the constructors within a class:

//check whether the constructors have the custom attribute ConstructorInfo[] constructorInfo = type.GetConstructors();

The GetConstructors method of Type returns an array of ConstructorInfo. Each instance of ConstructorInfo in the array contains details such as the name and attributes decorating that constructor, for each constructor of the class that Type represents. Next, we iterate through the array and get the list for DefectTrackerAttribute for the current constructor as shown in the following statements:

foreach (var item in constructorInfo) { IEnumerable<DefectTrackerAttribute> attributes = item.GetCustomAttributes<DefectTrackerAttribute>(); details.Append(GetMemberDetails(item.Name, attributes)); }

Then, we passed the list to the GetMemberDetails method along with the name. In the GetMemberDetails method, we iterate over the list to get the defect details using the properties of DefectTrackerAttribute as shown:

foreach (var attribute in attributes) { sb.Append("ID-"); sb.Append(attribute.DefectID); sb.Append("\t"); sb.Append("Resolved By-"); sb.Append(attribute.ResolvedBy); sb.Append("\t"); sb.Append("Resolved On-"); sb.Append(attribute.ResolvedOn); }

Similar to the constructor, we can get details of methods tagged with DefectTrackerAttribute using the GetMethodInfo method of the Type class. That is what we have done in the following statements:

/check whether the methods have custom attribute MethodInfo[] methodInfo = type.GetMethods(); foreach (var item in methodInfo) { IEnumerable<DefectTrackerAttribute> attributes = item.GetCustomAttributes<DefectTrackerAttribute>() if (attributes.Count() > 0) { details.Append(GetMemberDetails(item.Name, attributes)); } }

The MethodInfo class contains the details of a method of the class represented by Type. The array returned by GetMethods() contains details of all the methods within the class. We iterated over the array and determined whether the method is tagged with the attribute or not. If it is tagged, that is, the attribute list contains one or more elements, we fetched the details, just like we did for the constructor(s). Once we got the details of the constructor and the methods, we returned the details as a string value.

In the TrackDefect class of DefectTrackerApp, we called the instantiated DefectTrackerProcessor and called the GetDetails method with the assembly path and the class name that was entered via the UI. This is done in the Click event handler of the Load button.

private void btnLoad_Click(object sender, EventArgs e) { if (!String.IsNullOrEmpty(txtPath.Text) && !String.IsNullOrEmpty(txtClassName.Text)) { txtDetails.Text = new DefectTrackerProcessor().GetDetails(txtPath. Text, txtClassName.Text); } }

There's more...

We saw how Type helps us to get details of constructors and methods of a class. Similarly, we can get details of the properties of a class using the GetProperties method. It returns an array of PropertyInfo. Each PropertyInfo holds the details of a specific property of the class. If you want details of only a specific property, call the GetProperty() method and pass the property name as argument.

.Net Framework 4.5 Expert Programming Cookbook Over 50 engaging recipes for learning advanced concepts of .NET Framework 4.5 with this book and ebook.
Published: January 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Using asynchronous file I/O for directory-to-directory copy

Asynchronous file I/O has been a feature of .NET from Version 1.1 onwards. However, the loops that the developer had to run to get it working were many. In Version 4.5, .NET introduced a new API that would make using asynchronous file operation easy. At the core of the API, we have two operators—async and await. This recipe will focus on using these operators to implement an asynchronous directory-to-directory copy utility.

How to do it...

The following steps will help you perform directory-to-directory copy using asynchronous file I/O:

  1. Launch Visual Studio .NET 2012. Create a project of type Class Library and name it CookBook.Recipes.Core.AsyncFileIO.

  2. Rename Class1.cs to Utils.cs.

  3. Open Utils.cs. Make the class public and static as shown:

    public static class Utils { }

  4. Add a public static method to the class and name it CopyDirectoryAsync. It will take two parameters – a string containing the source directory and another string containing the target directory. It will return a Task value of type int. The signature will be as follows:

    public static Task<int> CopyDirectoryAsync(string sourceDir, string targetDir) { }

  5. Change the method signature to add the async keyword to it:

    public static async Task<int> CopyDirectoryAsync(string sourceDir, string targetDir) { }

  6. Add a variable of type int to the method. Assign the count of the files in the target directory:

    int count = Directory.EnumerateFiles(targetDir).Count();

  7. Next, add the following code:

    foreach (string filename in Directory.EnumerateFiles(sourceDir)) { using (FileStream sourceStream = File.Open(filename, FileMode. Open)) { using (FileStream DestinationStream = File.Create(targetDir + filename.Substring(filename.LastIndexOf('\\')))) { await sourceStream.CopyToAsync(DestinationStream); } } }

  8. Add the return statement as shown:

    return (Directory.EnumerateFiles(targetDir).Count() - count);

    Next, let us look at how to use the Utility class. To use the Utility class, we will create a Windows Forms Application project.

  9. Add a project of type Windows Forms Application and name it AsyncFileIO.

  10. Add a reference to CookBook.Recipes.Core.AsyncFileIO.

  11. Rename Form1.cs to FileUtility.cs.

  12. Open FileUtility.cs in the design mode. Design the form so that it looks like the following screenshot:

  13. Name the controls as detailed in the following table:

    Control

    Description

    Name

    Textbox

    To hold the path of the source directory

    txtSource

    Button

    To display the directory chooser for choosing the source directory

    btnSource

    Textbox

    To hold the path of the target directory

    txtTarget

    Button

    To display the directory chooser for choosing the target directory

    btnTarget

    Button

    To start copying from the source to the target directory

    btnCopy

    Folder Browser Dialog

    To display the folder chooser

    diagFolder

  14. Double-click on btnSource to add the Click event handler for it. Add the following code to the event handler:

    if (diagFolder.ShowDialog() == System.Windows.Forms.DialogResult. OK) { txtSource.Text = diagFolder.SelectedPath; }

  15. Switch to the design mode. Add the Click event handler for btnTarget by double-clicking on it. In the event handler, add the following code:

    if (diagFolder.ShowDialog() == System.Windows.Forms.DialogResult. OK) { txtTarget.Text = diagFolder.SelectedPath; }

  16. Similarly, add the Click event handler for btnCopy. Then, add the following code to the handler:

    if (!String.IsNullOrEmpty(txtSource.Text) && !String. IsNullOrEmpty(txtTarget.Text)) { Utils.CopyDirectoryAsync(txtSource.Text, txtTarget.Text); }

  17. Press F5 and run the application. Click on the Source button to choose the directory that you want to copy. Choose the directory to which you want to copy by clicking on the Target button. Then click on the Copy button to start copying.

That completes the steps to create directory-to-directory copy functionality, which does the copying asynchronously.

How it works...

The whole logic of asynchronous copy is implemented within one method: the CopyDirectoryAsync method of the Utils class. As you have already seen, both the class and the method are public as well as static. The reason for making the method static is that we have implemented it as a utility method. Utility methods are always implemented as public and static methods. In .NET itself, all the methods of the Math class are static methods, and the class itself is static.

Now let us look at how the logic works. If you observe the signature of the method, there are two things that make it different from other methods (or synchronous methods).

public static async Task<int> CopyDirectoryAsync(string sourceDir, string targetDir)

First is the async keyword. It means that somewhere in the method an asynchronous task is going to be executed. Next is the return type. If you want to return any value from a method that has async in its signature, you will have to do it using the Task object. In our case, we wanted to return the number of files copied. So we have used Task<int> as our return type.

As we wanted to return the number of files copied, we will have to know the current number of files in the target directory. With the following statement we can achieve this:

int count = Directory.EnumerateFiles(targetDir).Count();

In the preceding statement, we have used the EnumerateFiles method of the Directory class to get a list of all the filenames within the target directory and then got the number of elements in that list. Next, we have to get the files we want to copy. For that, we iterate over the filenames returned by Directory.EnumerateFiles. To EnumerateFiles, we passed the path of the source directory as shown:

foreach (string filename in Directory.EnumerateFiles(sourceDir)) { }

Then, we open each file for reading as a stream:

foreach (string filename in Directory.EnumerateFiles(sourceDir)) { using (FileStream sourceStream = File.Open(filename, FileMode.Open)) { } }

Once we have opened the file to be copied, we have to open another stream to the location where the file will be copied. To do that we created a file of the same name and then connected a new stream to it, as shown in the following highlighted code:

foreach (string filename in Directory.EnumerateFiles(sourceDir)) { using (FileStream sourceStream = File.Open(filename, FileMode.Open)) { using (FileStream DestinationStream = File.Create(targetDir + filename.Substring(filename.LastIndexOf('\\')))) { } } }

Now comes the most important part of our code, the statement that makes asynchronous copy work. Once we open a stream to a file in the destination directory, we can transfer the contents. To do so, we used the CopyToAsync method of the FileStream class. However, what makes the content transfer statement important is the await keyword before it, as in:

await sourceStream.CopyToAsync(DestinationStream);

The await keyword in the preceding statement tells the framework that the execution of this method is suspended until the CopyToAsync method is done. Apart from that, the await keyword also tells the framework to return the control of the execution to the code that called this method, that is, CopyDirectoryAsync. In other words, until the current file is copied, the application can resume its normal operation and would not appear to the user as if the application is frozen.

In our case, the FileUtils class calls the CopyDirectoryAsync method when Copy is clicked. When the execution reaches the await statement, the control is returned back to the FileUtils class until the current file, as per the loop, is copied. Till the file is copied, the user can make use of any feature of the application. Once the file is copied, the file in the source directory is opened and the process continues. If the size of the files are huge, say 500 MB, you will be able to see the effect of the asynchronous transfer.

Accessing JSON using dynamic programming

In Version 3.5, .NET introduced the var keyword. With var, developers got the choice of not declaring the type of the variable. It became the task of the compiler to infer the type of the variable based on the value assigned. .NET 4.0 took this concept a step ahead by introducing the keyword dynamic.

When a variable is declared dynamic, its type is inferred only during execution. The compiler does not check for the type and type safety of a dynamic variable. This helps a lot when dealing with data whose type is either unknown or too complex to be bound to a compiled object. In this recipe, you will see how dynamic can access parsed JSON data without creating classes for the JSON elements. One thing to keep in mind is that in this recipe the implementation of logic and the application that uses the implementation are one and the same. In other words, the main application itself contains the logic.

How to do it...

The following steps will help you access JSON using dynamic programming:

  1. In Visual Studio .NET 2012, create a new project of type Windows Forms Application. Name it DynamicJsonParsing.

  2. Rename Form1.cs to AccessJson.cs.

  3. Open AccessJson in the design mode. Design the form so that it looks like the following screenshot:

  4. Name the controls as detailed the following table:

    Control

    Description

    Name

    Label

    To display the name of the element whose value will be shown

    lblValueFor

    Label

    To display the value of the element

    lblValue

    Label

    To display the name of the complex element whose value will be shown

    lblComplexValueFor

    Label

    To display the value of the complex element

    lblComplexValue

    Textbox

    To display the JSON string being parsed and accessed

    txtJson

    Button

    To parse, access, and display the values of JSON data

    btnParse

  5. Add a reference to System.Web.Extensions.

  6. Switch to the code view mode and add the following import:

    using System.Web.Script.Serialization;

  7. Next, add the following private method to the class. It will return the JSON data.

    private string GetJsonString() { return @"{ 'order':{ 'name':'testOrder', 'value':'1000', 'products':[ {'name': 'testProduct', 'expiry': '12 months' }] }, 'delivery':'at home' }"; }

  8. In the constructor, add the following statement after the call of the InitializeComponents method:

    txtJson.Text = GetJsonString();

  9. Switch to the design mode. Double-click on the btnParse button to add a Click event handler.

  10. In the event handler, add the following statements:

    var serializer = new JavaScriptSerializer(); var dictionary = serializer.Deserialize<Dictionary<string, dynamic>>(txtJson.Text); lblValueOf.Text = "Value of delivery"; lblValue.Text = dictionary["delivery"]; lblComplexValueOf.Text = "name of product of order"; lblComplexValue.Text = dictionary["order"]["products"][0]["name"];

  11. Press F5 and run the application. Click on the Parse button. The values will be displayed as shown:

How it works...

The core work of accessing JSON happens in the event handler for the Parse button. We had assigned a string containing JSON. The data within the JSON string is about a particular order. The product contained in the order and the type of delivery is as shown:

{ 'order':{ 'name':'testOrder', 'value':'1000', 'products':[ {'name': 'testProduct', 'expiry': '12 months' }] }, 'delivery':'at home' }

In the JSON above, delivery is a normal data element. However, the name of a product is a complex data element since name is a part of the products array (note the square bracket), which itself is part of order element. For data of this type, if we go for the traditional approach to access the values, we will have to either create multiple classes or perform complex string manipulations. That is where having dynamic helps.

In the event handler for the Parse button, we first parsed the JSON using JavaScriptSerializer as shown:

var serializer = new JavaScriptSerializer(); var dictionary = serializer.Deserialize<Dictionary<string, dynamic>>(txtJson.Text);

The JSON data is deserialized or parsed into a dictionary having a string value as key and a dynamic object as value. If we look at the dictionary as a table of Key and Value, it will look something like the following:

Key

Value

delivery

at home

order

{

        'name':'testOrder',

         'value':'1000',

           'products':[

                      {'name':

'testProduct',

                         'expiry': '12

months'

                         }]

   }

 

From the preceding table it is clear that the value for delivery is at home. So the following statement is nothing special, just a simple way of getting the value via the key:

lblValue.Text = dictionary["delivery"];

However, if we have to find the name of the product, simply using the key won't work. If we pass the order as the key, we will get a complete piece of complex JSON data. However, this data is of type dynamic. So, if we write statements like the ones written as follows, the compiler won't complain, and will leave it to the runtime check to assign the values:

dynamic order = dictionary["order"]; dynamic products = order["products"]; dynamic product = products[0]; dynamic name = product["name"];

The first statement assigns the value of the order to the order variable. The order variable will now contain an array of products. The first element of the product array is assigned to the product variable. Then, from the product variable, the name key is used to access the product name. On combining all four steps, we get:

lblComplexValue.Text = dictionary["order"]["products"][0]["name"];

So, by using dynamic programming, we were able to access the parsed JSON data without having to create the class hierarchies and without having to use string manipulation.

One point to keep in mind is that dynamic methods and statements are not compiled. This, errors will only be caught at runtime when the statements are compiled and executed.

Summary

So, in this article we covered the core concepts in .NET, which included metadata programming, reflection, asynchronous I/O, and dynamic programming through various recipes.

Resources for Article :


Further resources on this subject:


About the Author :


A P Rajshekhar

 

A. P. Rajshekhar, Senior Developer with Vectorform, has worked on enterprise-level web applications and game development. His endeavors include development of a Learning Management System, a Supply Management Solution and Xbox-based games. He holds a Masters Degree in Computer Applications. He is a regular contributor to Devshed Portal on topics ranging from server-side development (JEE/.Net/RoR) to mobile (Symbian-based) development and game development (SDL and OpenGL) with a total readership of more than 1.4 million.

Contact A P Rajshekhar

Books From Packt


ASP.NET jQuery Cookbook
ASP.NET jQuery Cookbook

ASP.NET Site Performance Secrets
ASP.NET Site Performance Secrets

ASP.NET Data Presentation Controls Essentials
ASP.NET Data Presentation Controls Essentials

ASP.NET 4 Social Networking
ASP.NET 4 Social Networking

OData Programming Cookbook for .NET Developers
OData Programming Cookbook for .NET Developers

 .NET 4.0 Generics Beginner’s Guide
.NET 4.0 Generics Beginner’s Guide

ASP.NET 3.5 Social Networking
ASP.NET 3.5 Social Networking

 ASP.NET 3.5 CMS Development
ASP.NET 3.5 CMS Development


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
s
h
B
e
1
t
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software