Archive for the '.net' Category

Jun 12 2010

cancel all event handlers on postback

Published by Raja Nadar under asp.net, c#

recently i had an interesting problem in my project.. which is an asp.net 3.5 web application. it consists of a monster page, which nests close to million user controls within it. these user controls post back at will, and sometimes whimsically too… the page itself had a RAD Navigation control, the wizard king.

now the one common object these user controls and the page itself, was working was my Entity object from Entity Framework. Now the page is basically a wizard, which keeps filling this object, and there are options to save this object explicitly, or create templated items out of it.

This same object (Business Entity) could also be worked by multiple users across different browser sessions/work stations. sort of a disconnected data set. Any persistent action done on this object updates the ‘Last Modified’ date time of this object. (locally and in the database)

My requirement was that, during every page load, i needed to check if this local object was the latest copy, and if stale, redirect the user to a “no donuts for you” page…

Now, in the normal scenario, i would write a simple method in the page load of my monster page, which checks if the object is latest, and if not, just do a

Response.Redirect(donutUrl, true);

But Murphy being who he is, never gives normal scenarios to developers. I had to pop up a Modal window to the user to inform that, “you are working on a stale entity and need to refresh your view.” And on the click of a confirmation, redirect the user.

So i used an AJAX Modal popup, and popped up the message. On the OK click, the user was redirected as i had wished for.

Now as much as it looks hunky-dory, a weird thing was happening… if the user clicked on the “Save My Object So Far” button, the page load did its task, of popping up the popup properly (tongue twisty?) but the event handler for the “Save” button continued to happen behind, and it saved the entity… that is not what i wish for on a Monday morning, and then the next morning, and then the next…

now i had to prevent any further operation after the popup… so i began to put the stuff between by ears into effect… as lame as they were, i thought of the following ideas: (if they are that)

  • have a boolean flag, and chain it across the event handlers, so that if the entity was modified, do not proceed with the event handling code. (lame…. if there are too many event handlers.. and there were)
  • end the page response, after the popup.. so that no further code gets executed after the page load method. but next to grabbing a candy from my 2 year old cousin quietly, i have not been able to do this.. either a blank page is rendered, or everything happens as unexpected… there are no half-measures with the asp.net page life cycle.. (i cannot use document.Write())
  • how about if i manage to suppress all the event handlers on the page, once i detect that the entity is stale.

the 3rd idea seemed to make sense, and i proceeded with a dummy page.. which rendered a label on page load. and then, a button click modified this label. then in the page load, i tried to do a

protected void Page_Load(object sender, EventArgs e)
{
    this.LabelMessage.Text = "Page Load";
    this.ButtonAction.Click -= this.ButtonAction_Click;
}
 
protected void ButtonAction_Click(object sender, EventArgs e)
{
    this.LabelMessage.Text = "Clicked Me, overwrote Page Load";
}

and holy guacamole… it worked.. the event handler was not executed…

now another problem i noticed, is that i don’t know which control posted back.. also, which event of the control caused the postback.. so i had to solve 2 problems..

  • identify the control that posted back
  • remove all event handlers for that control..

and the doors of a solution seem to open up… finding the postback control was a standard snippet i use, and reflection rocks, when i have to dig out the protected “Events” property of any Control. i also realized that, the event handler delegates are stored in a linked list format for every event… which also need to be retrieved using reflection..

so i wrote a utility method, which, if given a page, finds the control that posted back, and removes all the event handlers of the control.

this way, you have a central generic method, which can suppress any further event handling in your code.

now that i have typed close to two pages of a newspaper, time to post the snippet, which you’ll scroll through, in the first place, in any case.. so here goes nothing…

// Cancels all the event handling code for the control that posted back.
public static void CancelPostbackEvent(Page page)
{
    if (page.IsPostBack)
    {
        var postBackControl = Utility.GetPostBackControl(page);
 
        if (postBackControl != null)
        {
            var controlType = typeof(Control);
 
            var postBackControlEventHandlerList = controlType.InvokeMember("Events", System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
            null, postBackControl, null) as EventHandlerList;
 
            if (postBackControlEventHandlerList != null)
            {
                var eventHandlerListType = typeof(EventHandlerList);
                object headEventHandlerListEntry = eventHandlerListType.InvokeMember("head", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
                null, postBackControlEventHandlerList, null);
 
                if (headEventHandlerListEntry != null)
                {
                    var delegatesDictionary = new Dictionary<object, Delegate[]>();
                    Utility.GetEventHandlersRecursively(delegatesDictionary, headEventHandlerListEntry);
 
                    foreach (var delegateContainer in delegatesDictionary)
                    {
                        for (var index = delegateContainer.Value.Length - 1; index &gt;= 0; --index)
                        {
                            postBackControlEventHandlerList.RemoveHandler(delegateContainer.Key, delegateContainer.Value[index]);
                        }
                    }
                }
            }
        }
    }
}
 
private static Control GetPostBackControl(Page page)
{
    Control postBackControl = null;
 
    var postBackControlName = page.Request.Params.Get("__EVENTTARGET");
 
    if (!String.IsNullOrEmpty(postBackControlName))
    {
        postBackControl = page.FindControl(postBackControlName);
    }
    else
    {
        foreach (string controlName in page.Request.Form)
        {
            var control = page.FindControl(controlName);
 
            if (control is System.Web.UI.WebControls.Button)
            {
                postBackControl = control;
                break;
            }
        }
    }
 
    return postBackControl;
}
 
private static void GetEventHandlersRecursively(Dictionary<object, Delegate[]> delegatesDictionary, object currentEventHandlerListEntry)
{
    if (currentEventHandlerListEntry != null)
    {
        var eventHandlerListEntryType = currentEventHandlerListEntry.GetType();
 
        var eventHandler = (Delegate)eventHandlerListEntryType.InvokeMember("handler", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
        null, currentEventHandlerListEntry, null);
 
        object key = eventHandlerListEntryType.InvokeMember("key", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
        null, currentEventHandlerListEntry, null);
 
        var nextEventHandlerListEntry = eventHandlerListEntryType.InvokeMember("next", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
        null, currentEventHandlerListEntry, null);
 
        if (eventHandler != null)
        {
            var eventDelegates = eventHandler.GetInvocationList();
 
            if (eventDelegates != null &amp;&amp; eventDelegates.Length &gt; 0)
            {
                delegatesDictionary.Add(key, eventDelegates);
            }
        }
 
        if (nextEventHandlerListEntry != null)
        {
            Utility.GetEventHandlersRecursively(delegatesDictionary, nextEventHandlerListEntry);
        }
    }
}

do let me know, if you have a better solution, or if the above code upsets your chihuahua…

my lack of conscience really pricks me, when i write new code..

One response so far

Feb 07 2009

C#.NET and MySql

Published by Raja Nadar under .net, c#, mysql

with the use of LINQ and Entity Framework, I haven’t been writing much ADO.NET code to for the Data Access layer. Until recently, when one of my projects in the solution is still .NET 2.0 based, with the standard ADO.NET Data Access. its been some time, since I saw the SqlConnection and SqlCommand classses.

 

offline, I was working with a pastime application of mine, demonstrating Data Source independence. It is a provider architecture, where the specific data source is easily pluggable.

 

The base interface (IDataProvider) is used by the application. I already had the SqlDataProvider defined for SQL Databases. I tried switiching the provider to a MySql data source. At the end of it, just wanted to publish a couple of snippets to do data access tasks using C# and MySql.

 

  • You can use the ADO.NET Driver provided by MySql. Download the latest MySql Connector for .NET from: http://dev.mysql.com/downloads/connector/net. This is a free developer version of the component.
  • You can download the appropriate connector based on the .NET version.
  • You can also use the Odbc Driver, but my snippet is for the MySql connector.
  • Add a reference to the MySqlData.DLL to your project. Do not forget to ship this DLL.

 

Add the following namespace:

using System.Data;
using MySql.Data.MySqlClient;

 

C# and MySql Data Access without a Transaction.

using (MySqlConnection connection = new MySqlConnection(mySqlConnectionString))
{
    using (MySqlCommand command = new MySqlCommand())
    {
        command.Connection = connection;
        command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "SELNewRecord";
 
        command.Parameters.AddWithValue("Param1", value1);
 
        connection.Open();
 
        using (MySqlDataReader reader = command.ExecuteReader())
        {
            // read the contents.
        }
    }
}

 

C# and MySql Data Access with a Transaction.

using (MySqlConnection connection = new MySqlConnection(mySqlConnectionString))
{
    using (MySqlCommand command = new MySqlCommand())
    {
        command.Connection = connection;
        command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "INSNewRecord";
 
        connection.Open();
 
        using (command.Transaction = connection.BeginTransaction())
        {
            command.ExecuteNonQuery();
            // Execute additional SQL Queries.
 
            command.Transaction.Commit();
        }
    }
}

 

after all the latest ORM technologies and ease of code writing, it feels nice to write a good old data access snippet. though only once every 12 months… 

3 responses so far

Dec 16 2008

rss and atom api in .NET 3.5

Published by Raja Nadar under .net 3.5, syndication

i love the features which creep into an API. and the .NET framework classes grow richer and richer. my latest random activity has been to develop User controls (blog rolls, rss widgets) in .NET 3.5.

to think of it, one can easily write an RSS reader control, with inline commenting feature for blog posts, searching by category, authors etc. all that, by just using the Syndication API in .NET 3.5.

 

here are the classes that i am talking about:

  • SyndicationFeed: this is the top-level class representing the RSS or ATOM feed
  • SyndicationItem: this represents a blog post
  • SyndicationCategory: a category defined in a blog, applied to posts
  • SyndicationLink: a link associated with a blog post. (self, edit, comments etc)
  • SyndicationPerson: author of the blog post (name, email, profile Url etc)

 

without further ado, here is a quick snippet to get you started on the RSS/Atom APIs..

firstly, a reference needs to be added:

System.ServiceModel.Web

the namespaces required are:

using System.ServiceModel.Syndication;
using System.Xml;

Code Snippet:

string atomOrRssUrl = "http://rajanadar.blogspot.com/atom.xml";
 
SyndicationFeed rssOrAtomFeed = null;
 
using (XmlReader feedXmlReader = XmlReader.Create(atomOrRssUrl))
{
    // Top-level feed in (Atom 1.0) or Rss 2.0
    rssOrAtomFeed = SyndicationFeed.Load(feedXmlReader);
 
    // Read the blog posts in this blog.
    foreach (SyndicationItem blogItem in rssOrAtomFeed.Items)
    {
        // Important Blog Post attributes.
        blogItem.Id;
        blogItem.Title.Text;
        blogItem.PublishDate;
        blogItem.LastUpdatedTime;
        blogItem.Copyright;
 
        // Contents
        if (blogItem.Content is TextSyndicationContent)
        {
            // typically, this will be HTML.
            (blogItem.Content as TextSyndicationContent).Text;
        }
 
        // Categories of this blog post.
        foreach (SyndicationCategory category in blogItem.Categories)
        {
            category.Name;
            category.Scheme;
            category.Label;
        }
 
        // Get the Links associated with this blog post.
        // e.g. Comments (replies), self, alternate, edit links etc
        foreach (SyndicationLink link in blogItem.Links)
        {
            link.Title;
            link.Uri;
            link.RelationshipType;
        }
 
        // Get the Authors associated with the blog post.
        foreach (SyndicationPerson author in blogItem.Authors)
        {
            author.Name;  // Raja Nadar
            author.Email; // noreply@blogger.com
            author.Uri;   // Profile Url for blogger
        }
    }
}

obviously, the code won’t compile :), not because i wrote it, but that i have just highlighted the relevant fields I used. not assigned or used them anywhere..

 

so explore the Syndication APIs and code happily ever after.

2 responses so far

Jul 24 2008

WCF, certificates, event logs and silly security exceptions

Published by Raja Nadar under security, wcf

my friend was working on certificate based WCF transport messages. she prototyped a demo, and was testing it out. she kept on hitting the following exception:

Found multiple X.509 certificates using the following search criteria: StoreName ‘My’, StoreLocation ‘LocalMachine’, FindType ‘FindBySubjectName’, FindValue ”. Provide a more specific find value.

the error message could not have been more concise.. I had a look at the code, and there was nothing programmatic to verify. it was all WCF configuration driven [that I like so much J].

the configuration for binding was as follows:

<bindings>

  <wsHttpBinding>

    <binding name=wsHttpEndpointBinding>

      <security mode=Message>

        <message clientCredentialType=Certificate />

      </security>

    </binding>

  </wsHttpBinding>

</bindings>

the configuration for service credentials was as follows:

<serviceCredentials>

  <clientCertificate>

    <certificate storeLocation=LocalMachine storeName=My

                 x509FindType=FindBySubjectName />

    <authentication revocationMode=Online trustedStoreLocation=CurrentUser />

  </clientCertificate>

  <serviceCertificate findValue=rajanadar.com storeName=My

                      storeLocation=LocalMachine

    x509FindType=FindBySubjectName />

</serviceCredentials>

I thought, the search ‘rajanadar.com’ may be returning more than one certificate from the store. (may be due to root certificates etc.., I don’t know)

I checked my certificate store, and gave a specific (unique) Subject Name and tried different things.

no luck, still the same issue.

after a little observation, I read the error message a little more carefully.. (why didn’t I do this the first time?)

Found multiple X.509 certificates using the following search criteria: StoreName ‘My’, StoreLocation ‘LocalMachine’, FindType ‘FindBySubjectName’, FindValue ”. Provide a more specific find value.

it complained of a blank ‘FindValue’

then it struck me that we missed the FindValue for the client certificate, not the service certificate.

The corrected configuration was:

<serviceCredentials>

  <clientCertificate>

    <certificate storeLocation=LocalMachine storeName=My

        x509FindType=FindBySubjectName findValue=uniqueclient.rajanadar.com />

    <authentication revocationMode=Online trustedStoreLocation=CurrentUser />

  </clientCertificate>

  <serviceCertificate findValue=server.rajanadar.com storeName=My

             storeLocation=LocalMachine x509FindType=FindBySubjectName />

</serviceCredentials>

that solved the issue. it was a simple silly mistake. (obviously only after it was caught)

 

the next error I encountered, sounded something like:

Unhandled Exception: System.Net.WebException: The underlying connection was closed: Could not establish secure channel for SSL/TLS.

 

Fortunately, my past sleight of hand on WSE and SSL certificates, quickly reminded me that, when dealing with Web Applications, I need to give sufficient access permissions to the aspnet user account, to the PFX files of the certificates.

I modified the access permissions of the PFX file in question, (yeah the \AppData\Microsoft\Crypto\RSA\MachineKeys path) and the application seemed to work without any more issues. silly things, nonetheless there’s a first time..

 

p.s. the aspnet user account permission issue reminds me of one more classic issue that I encountered most of the times..

[SecurityException: Requested registry access is not allowed.] 

Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)

System.Diagnostics.EventLog.FindSourceRegistration(String source, String machineName, Boolean readOnly)

System.Diagnostics.EventLog.SourceExists(String source, String machineName) +79

System.Diagnostics.EventLog.SourceExists(String source)

 

this is again because, creating a new event log or event source, needs registry write permissions, typically not possessed by the aspnet account.

 

Solution: initially, I used to grant write permissions to the registry keys

(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\NewLog or HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\Source)
 

But then, this is not a good security approach. during the lifetime of the application, it just needs to write to the event logs and never create new ones. Hence I created the new registry keys (effectively new event logs/sources) using my Installers, and granted read permissions to the web application accounts. this sounded good.

 

there’s a solution to every problem; given enough time and sometimes, well, just time…

11 responses so far

May 21 2008

SQL Alphanumeric Unique Keys

Published by Raja Nadar under .net

a small SQL snippet to generate a unique alphanumeric key. you have to feed in an existing key value and the script will give you the next value.

i had written this snippet way back, for a legacy application in which a simple GUID or auto-incrementing big ineteger could not be used as the primary key.

i just rewrote it in the Katmai IDE, and the first feel of native intellisense in SQL studio is refreshing.

here goes the snippet:

 

Declare @currentKey varchar(32)
Declare @nextUniqueKey varchar(32)
Declare @currentKeyLength int
Declare @counter int
Declare @newKeyHasCarry bit
Declare @singleChar char(1)
 
-- Let us assume, we have a current key and we are looking for
-- the next incremental unique key.
SELECT @currentKey =  'Test129'
 
SET @currentKey = UPPER(@currentKey)
SELECT @currentKey	    
 
-- Logic to build the incremented AlphaNumeric key value, which is unique.
 
SET @counter = 0
SET @nextUniqueKey = ''
SET @newKeyHasCarry = 0
SET @currentKeyLength = Len(@currentKey)
 
-- Iterate through each character in our current key to find the increment.
-- We'll have a carry, if we see '9' as the last character.
While @counter &lt;= @currentKeyLength
Begin
 
SET @singleChar = SubString(@currentKey, @currentKeyLength-@counter, 1)
 
-- We need to increment only if there is a carry, or if it is our first iteration.
IF (@newKeyHasCarry = 1) OR (@counter=0)
Begin
        -- Increment 9 to 'A'
		IF IsNumeric(@singleChar) = 1
		Begin
				IF @singleChar = '9'
					 Begin
					  SET @singleChar = 'A'
					  SET @newKeyHasCarry = 1
					 End
				Else
					 Begin
					  SET @singleChar = @singleChar + 1
					  SET @newKeyHasCarry = 0
					 End
		 End
		Else
		Begin
 
			IF @singleChar='Z'
			 Begin
			  SET @singleChar = '0'
			 End
			Else
			 Begin
			  SET @singleChar = Char(ASCII(@singleChar) + 1)
			 End 
 
			SET @newKeyHasCarry = 0
		End
End
 
-- Append the new character, to construct the key in a reverse direction.
SET @nextUniqueKey = Cast((@nextUniqueKey + @singleChar) AS varchar(255))
SET @counter = @counter + 1
End
 
-- Reverse the key to get the proper unique key.
SET @nextUniqueKey = Reverse(@nextUniqueKey)
 
SELECT UPPER(@nextUniqueKey)
-- End of snippet
-- End of snippet

Notes:

  1. After ‘Z’, the next incremental character is assumed to be ‘0′ [zero]
  2. The snippet is case insensitive.
  3. I like the in-built intellisense of SQL 2008. getting rid of external tools is nice.

 

there’s a solution to every problem; given enough time and money..

No responses yet

May 20 2008

X509Certificate properties

Published by Raja Nadar under WSE, c#, security

as part of some WSE implementation, I had a small utility to read the details of a X509 Certificate. especially the SKID (Subject Key Identifier), of the certificate. actually, WSE comes with a certificate reader tool, which reads the SKID of the certificate.

however, i had 2 issues, using this tool:

  • i needed to read the properties from a file, which was the  X509 certificate, instead of reading it from the certificate stores.
  • i also needed a string representation of the certificate to be stored in the database. (i like the idea of a database oriented certificate management)

In order to read the X509Certificate properties, there are 2 namespaces available.

 

using Microsoft.Web.Services2.Security.X509;
using System.Security.Cryptography.X509Certificates;

 however, of the 2 namespaces,  the Microsoft.Web.Services2.Security.X509 seems to give the Subject Key Identifier of the certificate. it makes all the more sense to use this namespace, when you are working with WSE enabled web services.

the code snippet to read the certificate properties: (certificate is assumed to be in a file location)

 

using (FileStream stream = new FileStream(certificateFilePath, FileMode.Open))
{
    byte[] blob = new byte[(int)stream.Length];
    stream.Read(blob, 0, (int)stream.Length);
 
    using (X509Certificate cert = new X509Certificate(blob))
    {
        this.textBoxBlob.Text = Convert.ToBase64String(blob);
        this.textBoxSubject.Text = cert.Subject;
        this.textBoxTokenIssuer.Text = cert.Issuer;
        this.textBoxSKID.Text = Convert.ToBase64String(cert.GetKeyIdentifier());
        this.textBoxExpiry.Text = cert.GetExpirationDateString();
    }
}

Notes:

  • You can get the WSE DLL from here.
  • the X509Certificate belongs to the Microsoft.Web.Services2.Security.X509 namespace.
  • Convert.ToBase64String(blob) is very useful if you want to store the certificate in a database field. it is one of the ways to eliminate certificate management, for your application.
  • The Subject Key Identifier is what uniquely identifies your certificate. when WSE is used in a declarative manner, typically the SKID, Subject and Token Issuer are used in the configuration files.

 there’s a solution to every problem; given enough time and money..

No responses yet

May 17 2008

multiple result sets in LINQ

Published by Raja Nadar under .net, c#, linq, sql 2005

I had a stored procedure which returned multiple result sets.

the stored proc in short, looked as follows: (trimmed down for relevant code)

 

CREATE PROCEDURE SELMessages
AS
BEGIN
 
   -- prefix blah blah
 
 Declare @Messages TABLE
 (
       MessageID int,
       MessageName nvarchar(256)
 )
 
 Declare @MessageRecipients TABLE
 (
       MessageID int,
       RecipientName nvarchar(256)
 )
 
    -- interim blah blah
 
    -- Result Set 1
    SELECT  MessageID, MessageName FROM @Messages
   
    -- Result Set 2
    SELECT  MessageID, RecipientName FROM @MessageRecipients
 
END
GO

using Linq to SQL (or DLinq or L2S) i had to use this stored procedure.
I dragged the SProc into the designer, and tried to use it in the code. the Linq to SQL designer had generated the following code:

 

[System.Data.Linq.Mapping.DatabaseAttribute(Name="MessageDB")]
public partial class MessageDBDataContext : System.Data.Linq.DataContext
{
 
 private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
 
        // more code
 
 [Function(Name="dbo.SELMessages")]
 public ISingleResult SELMessages()
 {
  IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
  return ((ISingleResult)(result.ReturnValue));
 }
 
        // more code
}

the result to be written was a class which looked like:

 

public partial class SELMessagesResult
{  
 private int _MessageID;
 
 private string _MessageName;
 
 public SELMessageRecipientsResult()
 {
 }
 
 [Column(Storage="_MessageID", DbType="Int NOT NULL")]
 public int MessageID
 {
  get
  {
   return this._MessageID;
  }
  set
  {
   if ((this._MessageID != value))
   {
    this._MessageID = value;
   }
  }
 }
 
 [Column(Storage="_MessageName", DbType="NVarChar(256) NOT NULL", CanBeNull=false)]
 public string MessageName
 {
  get
  {
   return this._MessageName;
  }
  set
  {
   if ((this._MessageName != value))
   {
    this._MessageName = value;
   }
  }
 }
}

my code snippet to use the Stored proc was as follows:

 

using (MessageDBDataContext messageDbDataContext = new MessageDBDataContext())
{
    return messageDbDataContext.SELMessages();
}

the above code, did not give me the expected behavior. it failed to identify the 2 result sets.

as you can see, the return type of the method is just the result SELMessagesResult,which represents our first result set only.

 I tried to solve the issue and found this post.

it pretty much solved my problem. the only difference is that, instead of the existing tables being returned, my Stored Proc returned  result sets based on some temporary tables it was creating.

based on the post, these were the steps, I followed:

  • created the following class to represent the first result set. the designer had already created this class. (SELMessagesResult)
  • created the following class to represent the second result set. SELMessageRecipientsResult)
 public partial class SELMessageRecipientsResult
{
    private int _MessageID;
 
    private string _MessageRecipient;
 
    public SELMessageRecipientsResult()
    {
    }
 
    [Column(Storage = "_MessageID", DbType = "Int NOT NULL")]
    public int MessageID
    {
        get
        {
            return this._MessageID;
        }
        set
        {
            if ((this._MessageID != value))
            {
                this._MessageID = value;
            }
        }
    }
 
    [Column(Storage = "_MessageRecipient", DbType = "NVarChar(256) NOT NULL", CanBeNull = false)]
    public string MessageRecipient
    {
        get
        {
            return this._MessageRecipient;
        }
        set
        {
            if ((this._MessageRecipient != value))
            {
                this._MessageRecipient = value;
            }
        }
    }
}
  • created a partial class obviously named same as my Data Context class. (right click on .dbml >> view code)
 partial class MessageDBDataContext
 {
 }
  • added a new method to this class with following signature:

 

public IMultipleResults SELMessagesWithMultipleRS()
  • Attributed the method with the following code:
[ResultType(typeof(SELMessagesResult))]
[ResultType(typeof(SELMessageRecipientsResult))]
[Function(Name = "dbo.SELMessages")]
public IMultipleResults SELMessagesWithMultipleRS()
  • the full method looks as follows: 
partial class MessageDBDataContext
{
    [ResultType(typeof(SELMessagesResult))]
    [ResultType(typeof(SELMessageRecipientsResult))]
    [Function(Name = "dbo.SELMessages")]
    public IMultipleResults SELMessagesWithMultipleRS()
    {
        IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
        return ((IMultipleResults)(result.ReturnValue));
    }
}
  • As the post explains, used the new method as follows:
using (MessageDBDataContext messageDbDataContext = new MessageDBDataContext())
{
    // Get both the result set.
    var results = messageDbDataContext.SELMessagesWithMultipleRS();
 
    // Get the first result.
    List messages = results.GetResult().ToList();
 
    // Get the second result.
    List messageRecipients = results.GetResult().ToList();
 
    // Do the necessary processing with the 2 sequences.
 
}

spot on.. all was in place and I tested the code for expected behavior.

notes:

  • since the original data context designer class is auto-generated by the LINQ to SQL designer, it is good to create a partial class. that way, you need not create it every time you change the DBML.
  • you don’t need to attribute the class with the ‘DatabaseAttribute‘ Attribute. .NET is happy with a single annotation.
  • you don’t need an instance of the DataContext in this class. (since it is a partial class)

hopefully, this issue will be resolved in their next release of Linq to SQL designer.

there’s a solution to every problem; given enough time and money.. 

No responses yet

May 14 2008

scan an image using the WIA Library

Published by Raja Nadar under .net, c#, wia

in .net,  2 ways of probably many more, to invoke imaging devices like scanner etc and get the scanned objects/documents are discussed below.

One is using TWAIN. TWAIN is an API used by software applications to talk to imaging devices. I believe it is an old API, but still quite famous. my multimedia courses always made reference to the TWAIN standards, and the section always ended with a reference citing, TWAIN to be very complicated and restricted to work with. 

 

The word TWAIN is from Rudyard Kipling’s “The Ballad of East and West” - “…and never the twain shall meet…” - reflecting the difficulty at the time of connecting scanners and personal computers. It was up-cased to TWAIN to make it more distinctive.

The word TWAIN is not an official acronym; however, it is widely known as “Technology Without Any Impressive Name.”  - Wikipedia

 

however, the second and the latest API is the WIA Library. (Windows Image Acquisition Library) this is a later API than TWAIN, and said to be more standards-conformant, though I have no idea yet. I haven’t worked with TWAIN to see the difficulties.

the WIA library is probably in-built in VISTA and Windows 2003 Operating systems, but I couldn’t spot it in XP.

getting onto the code, in order to scan and process a document using c#,

  • add a reference to the WIALib.dll (Microsoft Windows Image Acquisition 1.01 Type Library)
  • include the following using directives. 

 

using System.Runtime.InteropServices;
using WIALib;

 I ended up with the following snippet which scans and processes the scanned document:

 

private void ScanDocuments()
{
    // WIA manager COM object.
    // Allows the user to select an imaging device like scanner/camera etc.
    WiaClass wiaManager = null;
 
    // WIA devices collection COM object.
    // The collection of imaging devices.
    CollectionClass wiaDevicesCollection = null;
 
    // WIA root device COM object.
    // Represents the selected imaging device.
    ItemClass wiaRootDeviceItem = null;
 
    // WIA collection COM object.
    // Collection of WIA Image items.
    CollectionClass wiaImageItems = null;
 
    // WIA image COM object.
    // Represents the first of our selected image.
    ItemClass wiaFirstScannedItem = null;
 
    try
    {
        // create COM instance of WIA manager
        wiaManager = new WiaClass();
 
        // call Wia.Devices to get all devices
        wiaDevicesCollection = (CollectionClass)wiaManager.Devices;
 
        // No Devices found.
        if (null == wiaDevicesCollection || 0 == wiaDevicesCollection.Count)
        {
            throw new Exception("No WIA devices found!");
        }
 
        // = Nothing for COM.
        object useDialogFlag = System.Reflection.Missing.Value;
 
        // User will select a root device here.
        wiaRootDeviceItem = (ItemClass)wiaManager.Create(ref useDialogFlag);
 
        // No device selected. Just return.
        if (null == wiaRootDeviceItem)
        {
            return;
        }
 
        // Get the list of images.
        wiaImageItems = (CollectionClass)wiaRootDeviceItem.GetItemsFromUI(WiaFlag.SingleImage, WiaIntent.ImageTypeColor);
 
        // If there is a problem, return.
        if (null == wiaImageItems)
        {
            return;
        }
 
        // We'll grab the first picture only.
        bool useFirstImageOnly = true;
 
        // Iterate through the images and select the first one.
        foreach (object wiaImageObject in wiaImageItems)
        {
            if ((useFirstImageOnly))
            {
                wiaFirstScannedItem = (ItemClass)Marshal.CreateWrapperOfType(wiaImageObject, typeof(ItemClass));
 
                // Get a temporary file name.
                string tempFileName = System.IO.Path.GetTempFileName();
 
                // Copy the scanned object to the temporary file, in a synchronous manner.
                wiaFirstScannedItem.Transfer(tempFileName, false);
 
                // Work with the tempFile.
 
                useFirstImageOnly = false;
 
                // If you want to get hold of all the scanned items,
                // remove the 'useFirstImageOnly' flag.
            }
 
            // Release the enumerated COM object image.
            Marshal.ReleaseComObject(wiaImageObject);
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Acquire from WIA Imaging failed: " + ex.Message);
    }
    finally
    {
        // Release the COM objects used.
 
        if (null != wiaFirstScannedItem)
        {
            Marshal.ReleaseComObject(wiaFirstScannedItem);
        }
 
        if (null != wiaImageItems)
        {
            Marshal.ReleaseComObject(wiaImageItems);
        }
 
        if (null != wiaRootDeviceItem)
        {
            Marshal.ReleaseComObject(wiaRootDeviceItem);
        }
 
        if (null != wiaDevicesCollection)
        {
            Marshal.ReleaseComObject(wiaDevicesCollection);
        }
 
        if (null != wiaManager)
        {
            Marshal.ReleaseComObject(wiaManager);
        }
    }
}

couple of things I noticed:

  • when I use the method to invoke the graphic device (scanner, digital camera etc), the imaging device GUI is launched. as good as if you invoked the scanner/digital camera device using Start >> etc..
  • once you scan N number of objects, the above code snippet selects just the first one.

as you can see from the comments, you can easily process all the scanned documents., instead of just the first one.

I was browsing through the TWAIN standards and looking for .NET snippets to scan documents, and wanted a cleaner solution to scan a document. the WIA code seemed a little compact.

there’s a solution to every problem; given enough time and money.. 

3 responses so far

May 09 2008

String.Format and HTML string

Published by Raja Nadar under .net, c#, css, reflector

I was working with an email component, where different mails needed to be sent based on business scenarios. these emails were html formatted, and localization prone.. hence the html content was stored in resource files. I like the strongly-type resources in .NET 2.0 and above.

the whole html was not static, and hence it had placeholders (format strings) to insert business data values, during their actual use.

so a code snippet to construct the email subject would look like: 

string mailSubject = String.Format(CultureInfo.CurrentCulture,
Resource. EmailSubjectKey, blahObject.Id, blahObject.Name);

when I was unit testing the code snippet, I found that the above line gave me a Format exception. I checked the resource string. the Resource.EmailSubjectKey apparently had a valid content with the format strings {0} and {1} for Id and Name. no issues so far..

curious for the behind the scene action, I reflected on the String.Format method, and found that it internally called the StringBuilder.AppendFormat method, which is as follows: 

 

public StringBuilder AppendFormat(IFormatProvider provider,
string format, params object[] args)
{
    // some top level blah blah..    
    while (index &lt; length)
    {
        ch = chArray[index];
        index++;
        if (ch == '}')
        {
            if ((index &lt; length) &amp;&amp; (chArray[index] == '}'))
            {
                index++;
            }
            else
            {
                FormatError();
            }
        }
        if (ch == '{')
        {
            if ((index &lt; length) &amp;&amp; (chArray[index] == '{'))
            {
                index++;
            }
            else
            {
                index--;
                break;
            }
        }
        chArray[num4++] = ch;
    } // some more blah..

at this point, I realized that curly braces might be the issue. I checked back my html and sure enough, I was using inline css. 

 

    &;lt;style type="text/css"&;gt;
        .title
        {
        margin-left: 10px;
        font-family: Calibri, Verdana, Tahoma;
        font-size: small;
        } 
    &;lt;/style&;gt;

the AppendFormat chartered into the CSS curly braces and hence,  the unit case failed and the Format exception. now I had 2 solutions for this:

  • attribute based styles: (further inline css)
  • con the AppendFormat method..

I had a single CSS class for the whole Email, and hence the following attribute based style works: 

 

&lt;body style="margin-left: 10px; font-family:
Calibri, Verdana, Tahoma; font-size: small;"&gt;

but if you are figuring out how to get past the AppendFormat method, notice the snippet: 

        if (ch == '{')
        {
            if ((index &lt; length) &amp;&amp; (chArray[index] == '{'))
            {
                index++;
            }

if the method finds 2 consecutive opening braces ‘{‘, it bypasses it and does not do any format string substitution. that means, the following works perfectly well.

 

&;lt;style type="text/css"&;gt;
        .title
        {{
        margin-left: 10px;
        font-family: Calibri, Verdana, Tahoma;
        font-size: small;
        }} 
&;lt;/style&;gt;

so now, the above snippet actually puts things in place. AppendFormat doesn’t wander into the CSS area. the CSS engine doesn’t mind the duplicate braces, either.

there’s a solution to every problem; given enough time and money..

No responses yet

May 09 2008

extract text from a pdf

Published by Raja Nadar under .net, c#, ikvm, pdfbox

The other day, one of my developer friend asked me for a trivial function.  At least it looked trivial to start with. The task was to simply read the raw text of a PDF file using c#.. a small function to do this. No formatting, no image considerations.. just a plain dump of the PDF text.

My default approach was to go for acrobat32.exe and read the text. However, I realized that an adobe installation, com server registration, other unforeseen grumpy issues etc, may hinder the use of the exe. Also, I wasn’t sure of the free adobe reader acrobat32.exe to do the task. I browsed through some other PDF libraries.

The PDFBox for .NET built using IKVM seemed apt for the simple task at hand. 

Here are the steps to get the PDF text:

  • Add reference to the IKVM.GNU.Classpath.dll and PDFBox-0.7.3.dll libraries in your project.
  • Place the IKVM.Runtime.dll and FontBox-0.1.0-dev.dll libraries in your bin folder. It seems IKVM.Runtime.dll is always required to be present in the runtime folder, but need not be referenced. The other DLL depends on the PDF, you are trying to read. In my case, I needed the FontBox-0.1.0-dev.dll to be present. (the WA DOL driving guide PDF)
  • Use the following method to get the text: 

 

    private static string GetPdfText(string pdfFilePath)
    {
        string extractedPdfText = null;
 
        org.pdfbox.pdmodel.PDDocument pdfDocument = org.pdfbox.pdmodel.PDDocument.load(pdfFilePath);
 
        if (null != pdfDocument)
        {
            org.pdfbox.util.PDFTextStripper pdfTextStripper = new org.pdfbox.util.PDFTextStripper();
 
            // actual extraction of the text.
            extractedPdfText = pdfTextStripper.getText(pdfDocument);
        }
 
        return extractedPdfText;
    }

 

This open source PDF library provides a host of PDF manipulation capabilities. If you are looking for PDF document merging, page separation, indexes, bookmark reading etc, this is the library. My friend was happy with the small method.. I was happy that someone built a .NET version of this good java PDF library.

there’s a solution to every problem; given enough time and money..

2 responses so far

Next »