Tuesday, August 30, 2011

Getting right app.config file in Office Addins

Recently we got one issue while deploying our project’s Office Add-in component in one of the testing machine which has Microsoft Office 2007. We first tested in Excel after initial debugging we could see that one of the registry reading from add in code  is failing.Then we started digging into the inner areas and could see that the registry key is getting formatted using a app.config value, not getting correctly formatted.After some time by putting couple of message boxes we confirmed that the excel add-in is taking app.config file of excel from the below location!!!

<Install Drive>:\Program Files\Microsoft Office\Office12\EXCEL.EXE.config

We tested the manifest files, registry keys etc…but didn’t get any clue. According to us the add in should take its own config file and never the excel’s config file.Used almost all the debugging tools we know but no luck.We even thought of putting our config entries in the excel.exe.config Smile.Since that is not the right way finally started asking google.It gave the reason and solution very quickly.It is very simple .We need to change a registry entry where we specify the vsto addin path. The change seems so silly

The manifest registry entry needs to be prefixed with file:///.ie instead of "[TARGETDIR]ExcelAddIn.vsto|vstolocal": we need to use "file:///[TARGETDIR]ExcelAddIn.vsto|vstolocal":

http://stackoverflow.com/questions/1671587/word-addin-not-reading-appsetting .According to Microsoft this is a performance fix.

Simple fix but it took our half day and it was a SaturdaySad smile

Tuesday, August 23, 2011

Simple .net trace

This is the basic lesson we all might have learned when we started .net. But when we enter into complex applications or gain more knowledge over the time, we might have started writing our own complex logging systems or the Microsoft Logging application block. I am sure that at certain point we again think about the simple ways. That happened to me last week when I had to debug communication between our project and a completely unknown single sign on provider.
The scenario is simple. We had to integrate our Silverlight web app with a sign on provider similar to google.In google systems, if we open gmail or orkut it will go to another url for getting username and password and after authentication, it will return to the requested application ie gmail or orkut url. This is the UI side which user sees.But if we speak technically from the programmer perspective, the authentication mechanism has to provide a token in the form of cookies which should be kept at the client side (browser) and for the subsequent requests the token should go along and the application(gmail or orkut) should verify that for validity.If we are the developers of both the systems, this is not at all a big task.
But in our case the authentication provider is fully unknown except its name. No idea what the auth provider returns and in which header and all.And on top of all these, we have no access to this authentication provider from our development machines and we are not in a position to install the same in dev environment. This means we need to log each and every thing which happens between our application and the authentication provider. Microsoft enterprise library is the logging mechanism in our application.But if we log each and everything using the logging block, our application will become a logging application which cannot serve the actual user requests.What ever we put as config to switch on or off the logging that checking will take some CPU cycles which may degrade the server side performance. Moreover I am not a big fan of Enterprise library in terms of performance.So what should be our solution?

Simple Trace.Write statements which can be avoided at the compile time for the production installer msi files.

How to configure the trace destination file in simple way?

We talk about the method which write the log .Now where will this text go? Below is a simple config which route the trace entries to a file.
   <system.diagnostics>
      <trace autoflush="true"  
             indentsize="1">
        <listeners>
          <add name="Listener1" 
               type="System.Diagnostics.TextWriterTraceListener"
               initializeData="E:\Temp\testtrace.txt" />
          <remove name="Default" />
        </listeners>
     </trace>
   </system.diagnostics>


Challenge – Differentiating our trace entries

Use the category when writing trace.Better use the category as <Namespace>.<TypeName>.<MethodName> and open or import the trace file in excel then filter rows based on category.


How to avoid the trace statements in production


If we give the binaries which have the Trace code it will cause performance issues even though we are not using any listeners. It may be very slight in terms of CPU cycles which takes to check whether the trace listener is available or not.This is negligible in most cases but it may come to effect when this logging code is running a server which is supposed to server thousands of request per second.So we must avoid the Trace.write lines from the binaries at the compile time.Its easy


In VS2010 Goto Project Properties->Compile->Advanced Compiler Settings->Uncheck “Define Trace Constant”

When to use

Use this only when you don’t have any other debugging techniques available (test servers)and your logging mechanism feels complicated. Never miss the opportunity to debug using Visual studio in your own machine
Smile

Updates

2021-11-03

Sample source code pushed to GitHub.

Tuesday, August 16, 2011

Using Generic Type variables as method arguments

Let me start with some theories before a practical scenario. We have base class named MyGenBase inherited from object which accept a generic variable and contains some Properties and there are many derived classes which are not generic.There is also a static method named Process() which needs to accept the base class as it is and manipulate the properties.ie the objects of derived classes will be coming into the Process() as arguments.So how to write the signature of the Process().If we simply use Process(proxy as MyGenBase(of T)) it cannot accept the generic variable  T.If we normally use just T as generic variable it cannot access the variables of MyGenBase.So the solution is to use the generic variable with constraint as follows.

Process(of T)(proxy as MyGenBase(of T)) .

Confused altogether? Sorry I am little bit weak in theory. Lets see a real life example.

Using ClientBase<T> as method arguement

This is about passing a ClientBase<T> type variable as method parameter. ClientBase<T> is a well known class and you might have  used or at least heard, if you have did anything in WCF.

Suppose if we have to add a particular behavior to the proxies which are created at the client side of our application based on some conditions we cannot use the application config files for sure.The only way left is add the behavior through code.For Eg:

Private Function CreateProxy() As IDataService
Dim proxy As new DataServiceProxy
AddMyBehavior(proxy)
Return proxy
End Function
Private Sub AddMyBehavior(ByVal proxy As DataServiceRemoteProxy)
'Somecondition If Then
Dim behavior = New MyBehavior()
proxy.Endpoint.Behaviors.Add(behavior)
' End If
End Sub




Here the DataServiceProxy is the derived class of ClientBase with the generic type as IDataService ie declaration as


Public Class DataServiceRemoteProxy
Inherits System.ServiceModel.ClientBase(Of IDataService)
Implements IDataService
End Class





This is very simple.But if we have more services such as EmployeeService,CustomerService etc…and we need to add the same behavior we need to think about a more generic way of AddMyBehavior.That means we need to accept common base class of DataServiceProxy and EmployeeServiceProxy which is nothing but ClientBase<T> where T may be IDataService or IEmployeeService.If we just use generic variable as T it cannot recognize the EndPoint property which is specific to the ClientBase.Since we don’t have a non generic base class of ClientBase most people uses object as parameter and writes non type safe code or dynamic keyword.

So the question here is how to make the AddMyBehavior to accept any proxy and add the behavior.In the first look it may be complicated and there will be a tendency to pass as object or use the dynamic keyword.But there is a typed way.See the below code snippet.




Friend Shared Sub AddMyBehavior(Of T As Class)(ByVal proxy As ClientBase(Of T))
'Some condition If () Then
Dim cookieBehavior = New MyCookieBehavior()
proxy.Endpoint.Behaviors.Add(cookieBehavior)
'End If
End Sub




Here the ClientBase class accepts only the reference types.That’s why the Class keyword came into play.




Happy Coding!!!

Tuesday, August 9, 2011

IIS log analysis

Recently I got a golden opportunity to go Microsoft campus located in Charlotte ,NC and work with Microsoft guys .It was related to capacity planning of our project.The project uses Silverlight technology and obviously there are WCF services to supply data to the Silverlight application.Our main aim was to do capacity planning for the WCF service side and also the database. There I got chance to work with some tools which are available for years but never used by me.One of the tool was IIS log parser.
As name implies it is the tool to parse iis logs using sql kind of queries.You can easily find out what are the top 10 file types your iis is serving or what are the top 10 urls which are requested more etc…If you are a web site production support person, this will be childish.But for a developer who just code web sites and haven’t seen how his code is performing in the production this will be a key for him to the production world.

What are IIS logs and where can I find?
These are the log entries which IIS maintain for the requests it receive and serve.by default the logs will be in the %SystemDrive%\inetpub\logs\LogFiles folder in your machine.The log file writing is normally separated by number of factors.They are generated per web site , per day etc…Also we have option to specify which all fields should be included in the log files. For example we can avoid client IP address and referrer from the logging and add number of bytes sent and received.Ok now developers who doesn’t seen this might be excited to see the logs and how to configure it. Just open your IIS and select the Logging option at the right side and double click. Rest is very simple as some clicks.A screen shot below.

For more details about how to configure and analyze the logs see the below links.

http://technet.microsoft.com/en-us/library/cc732079%28WS.10%29.aspx
http://blogs.iis.net/carlosag/archive/2010/03/25/analyze-your-iis-log-files-favorite-log-parser-queries.aspx

Wednesday, August 3, 2011

Collation and the temp tables

It’s a hack which I am going to describe in this post for the people who are working in tight delivery schedules. Hope everybody knows what is collation in SQL Server and how it cause issues if we use database objects in different collations in a query. Also note the task involved in changing an existing SQL Server instance to a different collation and how to change collation of system databases such as temp database or any other user database.

We were using SQL_Latin1_General_CP1_CI_AS collation till couple of months back and recently moved to Latin1_General_100_CI_AS_KS_WS collation. We created new database in Latin1_General_100_CI_AS_KS_WS and it worked in all the development machines without any issue .So we changed the testing servers to Latin1_General_100_CI_AS_KS_WS collation and it performed well. After some load testing we had to modify one SP which introduced temp tables. As it is related to load testing we first applied in the test server and it rocked. But when we take the same sp to development machines the a problem raised because we didn’t change the sql server instances of our development machines to Latin1_General_100_CI_AS_KS_WS which is a good time consuming process. That means the temp database is in different collation compared to our project database.

For example consider a simple scenario.We created a Address database in the Latin1_General_100_CI_AS_KS_WS collation where our sql server instance is in SQL_Latin1_General_CP1_CI_AS .We have a Person table (Id,Name) and Address Table (Id,PersonId,Address) which has person id as foreign key and we need to select details of some persons based on a particular person name list.Earlier we had a ‘in’ keyword based implementation and that we changed to temp table based implementation where the temp table is a table which has one column and it will be joined with the Address table to get the details. Ok its time to see some sql.

CREATE TABLE [dbo].[Person](
[Id] [int] NOT NULL,
[Name] [nvarchar](255) NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)



CREATE TABLE [dbo].[Address](
[Id] [int] NULL,
[PersonId] [int] NULL,
[Address] [nvarchar](max) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Address] WITH CHECK ADD CONSTRAINT [FK_Address_Person] FOREIGN KEY([PersonId])
REFERENCES [dbo].[Person] ([Id])

Here is the modified query to use the table variables which uses temp db.

IF NOT OBJECT_ID('tempdb..#Selected') IS NULL
DROP TABLE #Selected;
CREATE TABLE #Selected
(
Name nvarchar(255)
);
--Logic to fill the #Selected table
select #Selected.Name,[Address].[Address]
from #Selected join Person
on #Selected.name = Person.Name
join [Address]
on Person.Id =[Address].PersonId;


The error msg was “Cannot resolve the collation conflict between "Latin1_General_100_CI_AS_KS_WS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.”. This means the temp DB is still in SQL_Latin1_General_CP1_CI_AS collation and our DB is in Latin1_General_100_CI_AS_KS_WS collation which doesn’t allow us to do a comparison on strings.So as a hack or quick work around in development environment, we modified the query as follows which specifies the collation on the comparison.
IF NOT OBJECT_ID('tempdb..#Selected') IS NULL
DROP TABLE #Selected;
CREATE TABLE #Selected
(
Name nvarchar(255)
);
--Logic to fill the #Selected table
select #Selected.Name,[Address].[Address]
from #Selected join Person
on #Selected.name collate Latin1_General_100_CI_AS_KS_WS= Person.Name collate Latin1_General_100_CI_AS_KS_WS
join [Address]
on Person.Id =[Address].PersonId;


Happy scripting…