Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Monday, May 21, 2012

XmlSerializationHelper

This is one of those helper classes that I keep using very often.

using System.IO;
using System.Text;
using System.Xml.Serialization;

public class XmlSerializationHelper<T> where T : class
    {
        public string Serialize(T objectToSerialize)
        {
            var sb = new StringBuilder();
            var serializer = new XmlSerializer(typeof(T));
            using (var writer = new StringWriter(sb))
            {
                serializer.Serialize(writer, objectToSerialize);
            }
            return sb.ToString();
        }

        public T Deserialize(string xml)
        {
            var serializer = new XmlSerializer(typeof(T));
            return (T)serializer.Deserialize(new StringReader(xml));
        }
    }

Usage:
string xml = new XmlSerializationHelper<SomeClass>().Serialize(obj);
AND
SomeClass obj = new XmlSerializationHelper<SomeClass>().Deserialize(xml);
Note: I am not using var here, just so that its clear what the return types are.

Wednesday, December 28, 2011

Room Finder - Outlook 2007 Add-in

Often, when we want to book a meeting room, we end up gathering all the rooms in the vicinity, removing those that are already booked, and then narrowing down our choices based on our preferred rooms. This sounds like quite a lot of manual effort each time we want to book a room. Fortunately, the Outlook APIs are good enough to allow us to automate most of this.

Introducing, the Room Finder - an add-in for Outlook 2007 users.

Sources of inspiration:
1. A large retailer's IT company that I used to work with had a Room Finder utility added in Outlook. This was back in 2008. I wasnt allowed access to the source code, though. So, I had to create one on my own, based on my experiences as an end-user.

2. Outlook 2010 has this feature. So, for those who haven't got a chance to use Outlook 2010 yet, this is a feature that you would be using once the upgrade happens. Using this add-in in Outlook 2007 makes you get used to this feature.

Steps to create the Room Finder add-in:
1. Using Visual Studio 2010, create an Outlook 2007 Add-in project

2. Add an Outlook Form Region.

Name it RoomFinderFormRegion instead of the default FormRegion1.
How would you like to create this form region?
Select 'Design a new form region'
What type of form region do you want to create?
Select 'Adjoining' type. Replacement is not supported for built-in forms.
What name, title and description do you want to use?
Name it 'Room Finder'
In which display modes should this form region appear?
Turn on the first checkbox and turn off the other 2. We only want this to be available in compose mode.
Which standard message classes will display this form region?
Turn on the first one checkbox for Appointment. Turn off the others, including the default one for Mail Message. We only want this for Appointments / Meeting Requests.
We don't need to specify any custom message classes either.
Click on Finish
(Optional) If you used the default name of FormRegion1 and want to rename it to RoomFinderFormRegion, rename it on the file instead of the class. This keeps the file and the class name in sync. For sake of completeness, there are some more regions where this name would need to be udpated. Find and replace all in the solution for 'FormRegion1'. You would find a string literal, some comments, a corresponding factory class and event handlers where this needs to be updated.
(Note) A pfx key would also be added automatically to the project. You don't need to worry about that.

3. Open the RoomFinderFormRegion in design mode. Add a normal (Windows Forms) Button from the toolbox. Change the Name and Text properties of this button to 'btnSuggest' and 'Su&ggest me a room...' respectively. The & is obviously just to make the g a hot-key so that one can get to it sooner by using the Alt-G key combination.
Ensure that you reduce any whitespace on the form region, except for the button. Resize the form region vertically. Resizing horizontally is not required, as the form region would occupy the entire width of the appointment item anyway.

4. Add a button click event handler. Include the following code in the event-handler.
            try
            {
                var item = (Outlook.AppointmentItem)this.OutlookItem;
                var mapi = Globals.ThisAddIn.Application.GetNamespace("MAPI");
                mapi.Logon();
                // get appointment time info
                DateTime startOn = item.Start;
                DateTime endOn = item.End;
                //TODO: sort rooms based on floor, user preferences & people count
                string identifiedRoom = string.Empty;
                foreach (var room in rooms)
                {
                    //get meeting room's calendar properties
                    var resource = mapi.CreateRecipient(room);
                    bool isFree = resource.AddressEntry.GetFreeBusy(startOn, endOn);
                    if (isFree)
                    {
                        identifiedRoom = room;
                        break;
                    }
                }
                mapi.Logoff();
                if (!string.IsNullOrEmpty(identifiedRoom))
                {
                    item.Resources = identifiedRoom;
                    item.Location = identifiedRoom;
                }
                else
                {
                    MessageBox.Show("No rooms found for this time period! Please consider scheduling the appointment at another time.");
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(string.Format("Error: {0}", ex.ToString(), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation));
            }
This code won't compile yet. Complete the following steps for that.

5. Add the following class-level member variable to the RoomFinderFormRegion class.
        private string[] rooms = new string[] {
            "Conf Room 1",
            "Conf Room 2",
        };
The array needs to contain all the rooms that we are intersted in. For each room, the string can either be the display name, alias or the full SMTP e-mail address of the meeting room.

6. Add a using statement for the MessageBox.
You can see a few more that were automatically added to the RoomFinderFormRegion class. Use Remove and Sort to remove the redundant ones. You will find that these are the only ones we need for now:
using System;
using System.Windows.Forms;
using Outlook = Microsoft.Office.Interop.Outlook;

7. If you attempt to use the in-built AddressEntry.GetFreeBusy method, it doesn't feel a lot DotNetty. It returns a string, with each character being a free/busy indicator for a certain time interval, the default being 30 minutes. The appointment, on the other hand, already has the start and end datetime values specified. So, what we actual need is for the AddressEntry object to give us a method that would tell us if the resource (meeting room) is available during that time. Since the AddressEntry object doesnt have a method of that kind, we use extensions. And for that, we need to put that in a separate static class, as shown here.
    public static class AppointmentHelper
    {
        private const int DEFAULT_INTERVAL = 30;
        public static bool GetFreeBusy(this AddressEntry addressEntry, DateTime start, DateTime end)
        {
            string freeBusyInfo = addressEntry.GetFreeBusy(start.Date, DEFAULT_INTERVAL, false);
            int position = (int) start.TimeOfDay.TotalMinutes / DEFAULT_INTERVAL;
            TimeSpan ts = (end - start);
            int blocks = (int) ts.TotalMinutes / DEFAULT_INTERVAL;
            return freeBusyInfo.Substring(position, blocks).All(c => c.Equals('0'));
        }
    }

8. Build the code. Ensure that the Outlook client has been closed. You can now run/debug the code. When you create a new appointment, you can now see the new region at the bottom part of the item window.
Click on the button in this Room Finder region to invoke the code that looks for a room which is free during the time selected in the appointment. If no rooms could be identified, a corresponding message is shown. Once a room is identified from the order in which it is present in the array, the search is called off, and the room is added as a resource to the appointment item.

9. Notice that I have left a TODO comment in the code above, which states that another feature could be built into this to sort the rooms based on location/floor, user preferences & people count. For instance, you wouldn't want to book a room which is good enough for 28 people if only 3 of you are going to use it. All this logic would execute to give a sorted list of the rooms array mentioned earlier. The rest of the code remains the same.

10. It is typical to expect the list of rooms to be configured externally instead of being hard-coded as an array of strings.
Add an application configuration file to the project. Include a key named Rooms and a value having a semi-colon delimited string of conf rooms. This is what it would look like.
<configuration>
  <appSettings>
    <add key="Rooms" value="Conf Room 1; Conf Room 2"/>
  </appSettings>
</configuration>

Add a reference to System.Configuration, and add a using statement for the same namespace. Add the following code at the top of the button click event hander, making sure this is still within the try block.
                //TODO: provide a way to edit and store this from Outlook
                string roomsDelimited = ConfigurationManager.AppSettings["Rooms"];
                rooms = roomsDelimited.Split(';');
You can also remove the array initializer since it is now being done only on the button click. The array definition can remain there.
private string[] rooms;
Again, I have left a TODO comment here since managing rooms from Outlook (another button in the add-in, plus storing this data in the mailbox) would be a better approach than having to manage it thru the config file.

11. Note that once you start the project, it will publish this add-in to Outlook. Any changes that you make to the add-in reflect in Outlook during the next launch. Even if you are launching Outlook separately after that, these changes are reflected.
To remove a certain add-in locally, you can use Tools → Trust Center → Add-ins.
Click on Go, select the add-in you don't want anymore (ignore the checkboxes - those are only for enable/disable) and click on Remove.

12. To deploy/distribute & for others to install
Use the Publish wizard. This also creates the setup file.
I suggest publishing to a website so that you can put in updates to the same place.
The default behavior is for the add-in to check for updates every 7 days.

References:
http://msdn.microsoft.com/en-us/library/bb410039(v=office.12).aspx
http://www.eggheadcafe.com/microsoft/Outlook-Program-Forms/35554170/how-to-read-single-instance-of-recursive-meetings-in-outlook.aspx
http://www.tech-recipes.com/rx/1959/outlook_2007_disabling_enabling_add_ins/
http://www.codeproject.com/KB/office/Outlook_Add-in.aspx
http://msdn.microsoft.com/en-us/library/bb147704(v=office.12).aspx
http://msdn.microsoft.com/en-us/library/ms526723(v=exchg.10).aspx
http://support.microsoft.com/kb/310259

Tuesday, December 27, 2011

OWASP Top 10 for .NET Developers

http://www.troyhunt.com/2011/12/free-ebook-owasp-top-10-for-net.html

This article includes a link to a free eBook​ that includes content from all the related blog posts.

Excerpts from the author:
I knew – and many of us know – that unsalted hashes are vulnerable to a rainbow attack but I’d never actually executed one of these attacks myself. So I did. Same again on sniffing packets; knowing that lack of transport protection leaves network traffic vulnerable is one thing, sitting in the car outside McDonald’s and actually capturing wifi traffic and hijacking the session (my own, that is!) is another thing altogether.

My analysis:
Completed just the first topic so far. It's nice to see a step-by-step approach that shows the vulnerabilities being exploited, and ways to reduce those risks.

Thursday, December 8, 2011

FlowDocument issues

We created this service that would fire prints on a network printer. When tested for performance, the code showed signs of memory leaks. This was strange, because I was expecting the .Net's garbage collector to cleanup unused objects. Perhaps there were some objects that were left to be disposed.

So, as part of our analysis, using WinDebugger, a memory dump of the process was collected. Using DebugDiag, this dump was analyzed. Multiple objects of type FlowDoument (used in CreateXpsDoc) were found blocking the finalizer queue. Googling for this type led us to some similar problems with this type being discussed in tech forums. It seems FlowDocument uses System.Windows.Threading.Dispatcher to free all resources

Reference: http://stackoverflow.com/questions/952985/flowdocument-memory-issue-in-c-sharp

Solution:
Execute these lines of code between printing and deleting the xps file.
var dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
dispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.SystemIdle);
System.Windows.Threading.Dispatcher.Run();

Wednesday, November 30, 2011

Rolling Flat File Trace Listener

Here are the standard trace listeners supported by Enterprise Library 3.1 Logging Application Block.

Table 7 mentions the Rolling Flat File Trace Listener. The flat file rolls over based on either the size of the file or the time interval (such as daily). You could also do both.

Friday, November 4, 2011

DateTime difference

In C#, if I have this
DateTime startedOn = DateTime.MinValue;
DateTime endedOn = DateTime.Now;
then
endedOn - startedOn is a valid expression that returns a TimeSpan with the difference between the 2 dates.

However, the same doesnt work in SQL Server. We need to use
DATEDIFF(s, startedOn, endedOn)
where s stands for seconds. So, if the 2 datetime values have a difference of 10 seconds and 1 minute, and if you expect 10, you are thinking Excel. The value returned here would be 70.

http://msdn.microsoft.com/en-us/library/ms174420.aspx

StringBuilder

We all know that a StringBuilder object should be used when we are concatenating a sizeable number of strings.

It seems the default capacity is 16. The default maximum capacity is 2147483647 (Int32.MaxValue), which is around 2 gigs, since Int32 can store 4G different values, from -2G to 2G-1.

The value of 16 was surprising. I thought it would be much higher than that. So, in my app where I need to load about 300MB into a StringBuilder object, I guess it is better to use it with a sizeable initial capacity. The initial capacity is also used as a delta when it runs out of the initial capacity. So, choose it wisely. I like to use a value that is a tradeoff between asking for a memory allocation every 16 bytes, and asking for all (like 300MB) in 1 or 2 allocations. For my current app, I have used it as 10MB.

StringBuilder sb = new StringBuilder(10 * 1024 * 1024);

Reference: http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

Wednesday, October 19, 2011

ConnectionState

I was writing this common method that helps in managing SQL Connections. I came to the point of closing and disposing of connections. I guess having a connection object in a using block will not do, since we wont be able to close the connection before it gets disposed. So, I initialized, opened and used it in a try block, and attempted to close it in the finally block, like this

            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                    connection.Close();
            }
I put in this additional validation to check for the connection state, perhaps from my WCF experience. When I explored the possible state of values in the System.Data.ConnectionState enum, it appears that other than Open and Closed, the 4 other states were "reserved for future versions of the product". Well, the only thing this validation protects from now is if the connection is attempted to be closed when it already is closed.
http://msdn.microsoft.com/en-us/library/system.data.connectionstate.aspx

Here's my DataHelper class:
    internal class DataHelper
    {
        private static readonly string sqlConnectionString = "ConnectionStringLiteral Or MethodCall Goes Here";
        internal static int ExecuteNonQuery(string storedProcName, SqlParameter[] parameters = null)
        {
            SqlConnection connection = null;
            int rowsAffectedCount;
            try
            {
                connection = new SqlConnection(sqlConnectionString);
                using (SqlCommand command = new SqlCommand(storedProcName, connection))
                {
                    SetCommandProperties(command, parameters);
                    rowsAffectedCount = command.ExecuteNonQuery();
                }
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                    connection.Close();
            }
            return rowsAffectedCount;
        }
        internal static DataSet ExecuteQuery(string storedProcName, SqlParameter[] parameters = null)
        {
            SqlConnection connection = null;
            DataSet dataSet = new DataSet();
            try
            {
                connection = new SqlConnection(sqlConnectionString);
                using (SqlCommand command = new SqlCommand(storedProcName, connection))
                {
                    SetCommandProperties(command, parameters);
                    new SqlDataAdapter(command).Fill(dataSet);
                }
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                    connection.Close();
            }
            return dataSet;
        }
        private static void SetCommandProperties(SqlCommand command, SqlParameter[] parameters)
        {
            command.CommandType = CommandType.StoredProcedure;
            if (parameters != null)
                command.Parameters.AddRange(parameters);
        }
    }

Identity field - int or not

I have an identity field in my SQL Server database's table. And I have this stored procedure which inserts a row in this table. The stored proc also returns a few other calculated values other than the new id created. So, I have a SELECT query at the end of the stored proc which goes like this

SELECT SCOPE_IDENTITY() as SomeId, @bar as SomethingImportant

And I use this code in C# to extract this id
int id = (int)rows[0]["SomeId"];

This gives me an invalid cast. Upon inspection, I notice that the value returned from the db call is decimal, not int! Why? Because SCOPE_IDENTITY, and even @@IDENTITY and IDENT_CURRENT(tablename) have the return type defined as NUMERIC(38, 0). This causes .Net to consider it as a decimal.

http://msdn.microsoft.com/en-us/library/ms190315.aspx
http://msdn.microsoft.com/en-us/library/ms187342.aspx
http://msdn.microsoft.com/en-us/library/ms175098.aspx

I ended up using it like this
int id = (int)(decimal)rows[0]["SomeId"];

UPDATE: In my second version, I assigned the value from SCOPE_IDENTITY() to a locally declared INT variable in the stored procedure, and returned that instead. That way, the C# code also takes it as in int.

Friday, October 14, 2011

New datatype - DateTime2

Want to know more about the datetime data type in SQL Server?http://technet.microsoft.com/en-us/library/ms187819.aspx

The first thing they will tell you is about the new datetime2 data type.
http://technet.microsoft.com/en-us/library/bb677335.aspx
"Use the datetime2 data type for new work. This type aligns with the SQL Standard. It is more portable."

The interesting thing is that it has more seconds precision with the same storage size of 8 bytes. In fact, it takes only 7 bytes for a precision of 3 or 4 digits, and 6 bytes for less than 3 digits.

datetime2 in SQL Server also has the same date range as that of DateTime defined in .Net, from
January 1, 0001 00:00:00.0000000
through
9999-12-31 23:59:59.9999999
as defined by DateTime.MinValue and DateTime.MaxValue
http://msdn.microsoft.com/en-us/library/system.datetime.minvalue.aspx
http://msdn.microsoft.com/en-us/library/system.datetime.maxvalue.aspx

The earlier datatype (datetime) in SQL Server has a lesser range and lesser precision.
January 1, 1753 00:00:00.000
through
December 31, 9999 23:59:59.997

So, beware when you use DateTime.MinValue or MaxValue in .Net and send it to a datetime parameter in SQL Server. Use datetime2 instead.

It seems System.Data.DbType.DateTime2 is also defined in .Net from framework 3.5 onwards.
http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx

Wednesday, October 12, 2011

Get caller's type

If I write a method in C#, can I get to know what type invoked this method? It seems we can, by inspecting the stack trace. But, a better design is just to accept a System.Type parameter and have the caller send that information.

Reference:
http://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class