Using extension methods to handle Xml

by Mikael Henriksson 16. February 2010 08:42

I have created a few very useful extension methods for handling the System.Xml.Linq namespace that I use all the time. I’ve had positive feedback on this before (and it showed up in this post) and now I had to create a couple of extra extensions just to make my life even easier. The way I work with these extensions is when I don’t care to handle null’s and just want to return the value of an element or an attribute who’s name I know.  If the value is an empty string you can bet I have a test that catches those scenarios. Like let’s say I want to read a configuration xml that I have created myself before hand. Or someone else’s xml that is very unlikely to change without my knowledge. I will put all this code up on git hub.

 

To show what I mean I’ll start with what I used to be doing.

var value = enumerableElements.Single(x => x.Name == "foo").Attribute("bar").Value;

With a bunch of the above statements in your code it becomes completely impossible to understand what the hell I was thinking / not thinking when I wrote the code. I write code for myself mostly and I am extremely picky with my code for that specific reason. What is really going on in Linq to Xml is like in most of the things that ships with .NET. It’s a very generic framework that you can build on top of and That’s the whole fun of it all. :)

So what we want to do above is to get the attribute value easily and we don’t care about checking for nulls, it should return the default value if we can’t find the attribute. Let’s start with from the end with getting the attribute value. That could (and will) look like:

public static string GetAttributeValue(this XElement element, string attributeName)
{
	XAttribute attribute = element.Attribute(attributeName);

	return attribute != null ? attribute.Value : string.Empty;
}

The above is the core, it should never throw any exceptions unless the situation of course is exceptional but in that case I don’t mind so much. The next part is debatable if it should be done but I do it anyway.

 

public static string GetAttributeValueFromElement(this IEnumerable<XElement> elements,
												  string elementName, string attributeName)
{
	var element = elements
		.Descendants(elementName)
		.FirstOrDefault(x => x.Name == elementName);

	return element.GetAttributeValue(attributeName);
}

Great stuff I just made my own life a bit easier but I believe I can take it one step further without breaking too many things. What if we easily could get rid of needed conversions after retrieving the string value and skip to getting the converted value? Imagine this code, it’s pretty common:

foo.ValidatedAt = DateTime.Parse(validation.GetElementValue("timestamp"));

We can semi-easily simply GetElementValue even more using generics. The above can be shortened to:

public static T GetAttributeValue<T>(this XElement element, string attributeName)
{
	XAttribute attribute = element.Attribute(attributeName);

	return attribute != null 
		? Converts.To<T>(attribute.Value) 
		: Converts.To<T>(string.Empty);
}

Now the call to GetAttributeValue would be shortened to.

foo.ValidatedAt = validation.GetElementValue<DateTime>("timestamp");

Hiding behind Converts.To is what I found in a comment from Tuna Tuksoz in this blog post. I’ll be honest with you I haven’t used the Type Descriptor before so I am not going to make a fool out of myself explaining it in detail but obviously it holds information about types and registers / gets converters for the types it knows about.

public static class Converts
{
	public static T To<T>(object value)
	{
		return (T)To(typeof(T), value);
	}

	public static object To(Type t, object value)
	{
		TypeConverter tc = TypeDescriptor.GetConverter(t);
		return tc.ConvertFrom(value);
	}

	public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
	{
		TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
	}
}

I would have named my class Convert but then I would have to use System.Convert in all other places. Maybe I should just switch to this generic one instead :)

Tags: , ,

C# | LINQ | Xml

Copying a specific subfolder of a parent folder

by Mikael Henriksson 14. February 2010 01:23

I have a service that sends our application to customers based on various criteria's. I want to verify the most important parameters and to do that I of course need to copy the whole darn folder structure to the directory the tests are run from to simulate the web client.

To please both Team City and the R# test runner I had to create something of my own and I’d rather not lock that down to visual studio nor NAnt. I found this great class on MSDN that seems to do the trick.

 

public class CopyDir
{
	public static void Copy(string sourceDirectory, string targetDirectory)
	{
		var diSource = new DirectoryInfo(sourceDirectory);
		var diTarget = new DirectoryInfo(targetDirectory);

		CopyAll(diSource, diTarget);
	}

	public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
	{
		if (source.Name.Contains(".svn"))
			return;
	   
		// Check if the target directory exists, if not, create it.
		if (Directory.Exists(target.FullName) == false)
		{
			Directory.CreateDirectory(target.FullName);
		}

		// Copy each file into it's new directory.
		foreach (FileInfo fi in source.GetFiles())
		{
			Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
			fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
		}

		// Copy each subdirectory using recursion.
		foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
		{
			DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name);
			CopyAll(diSourceSubDir, nextTargetSubDir);
		}
	}
}

All I did was add a filter for “.svn” folders that I really don’t want to keep. I do want to delete the whole thing after the tests are run. I also added a method for recursively navigating the parents to find the desired folder.

public DirectoryInfo RecursivelySearchParents(DirectoryInfo startDir, string folder)
{
	var resultDir = startDir.GetDirectories().FirstOrDefault(x => x.Name == "sw");
	if (resultDir != null)
		if (resultDir.Exists)
			return resultDir;
			
	return RecursivelySearchParents(startDir.Parent, folder);
}

And then the SetUpFixture with it’s SetUp and TearDown attributes.

[SetUpFixture]
public class TestSetUp
{
	public static string BinaryTargetDir = 
		string.Intern(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "sw"));
	public static string SourceDirectory;

	[SetUp]
	public void Setup()
	{
		var dir = new DirectoryInfo(Directory.GetCurrentDirectory())
		SourceDirectory = CopyDir.RecursivelySearchParents(dir, "sw").FullName;
		CopyDir.Copy(SourceDirectory, BinaryTargetDir);
	}
	
	[TearDown]
	public void TearDown()
	{
		var dir = new DirectoryInfo(BinaryTargetDir);
		dir.Delete(true);
	}
}

Tags:

C#

How should I use AutoMapper?

by Mikael Henriksson 13. February 2010 16:12

It is actually really simple and I have to thank my boss, DBA and friend Fridthjof for that. If you ever thought it is difficult to work with a DBA then imagine having one as your boss! *joke*  

He’s a great guy and as a DBA he is actually very understanding and I have been able to use pretty much any tool I want to get the job done as long as I don’t use CamelCase names in the database. Since we are using whatever tools we feel might get us there the quickest/easiest we have gone down different paths where I have spent a bit more time setting things up with NHibernate whereas my colleague has chosen to move quicker initially with LINQ to SQL  and use Fridthjof to do the queries in form of stored procedures for him. It’s worked out great for both of us though for me it took a while longer but yesterday when we where going to synchronize  our works I found that in the other projects he ran in to having to do all the work two times or so because of the different naming standards in the database and C#. I instantly came to think of AutoMapper so I suggested it, helped him set it up for what I needed from him (took a couple of minutes until it was deployed) and I am pretty sure that I will have his eternal gratitude for showing him AutoMapper. Question is how do I setup AutoMapper? Example below:

 

[Test]
public void AutoMapper_configuration_should_be_valid()
{
	Mapper.CreateMap<ContactQuery, ContactInfo>()
		.ForMember(dest => dest.SomeProperty, opt => opt.Ignore());

	Mapper.CreateMap<ContactInfo, ContactQuery>()
		.ForMember(dest => dest.FirstVersionAt, opt => opt.Ignore())
		.ForMember(dest => dest.LastVersionAt, opt => opt.Ignore())
		.ForMember(dest => dest.MaxResult, opt => opt.Ignore());
	
	Mapper.AssertConfigurationIsValid();
}

 

You can of course do much more than this with AutoMapper but it’s a good start the above is a test verifying that the maps I just setup is valid. don’t worry. AutoMapper will tell you exactly what your mapping problems are. This type of mapping should of course not be confused with NHibernate mappings but they are basically the same thing between two objects instead of between one object and one table.

So how do I use this map then?

var contact = new ContactInfo { ContactId = 1 };
var query = Mapper.Map<ContactInfo, ContactQuery>(contact);

So far so good BUT! This is doing the same thing / writing the same code in two places and you should really not repeat yourself. Time to DRY up and place that in a Profile of the project using it. (I know the documentation is blank =:])  That way it’s really easy to add a test for it and reset the test afterwards so that we at least keep the Assertions to just validating the bare minimum.

public class ContactProfile : Profile
{
	protected override void Configure()
	{
		CreateMap<ContactInfo, ContactQuery>()
			.ForMember(dest => dest.FirstVersionAt, opt => opt.Ignore())
			.ForMember(dest => dest.LastVersionAt, opt => opt.Ignore())
			.ForMember(dest => dest.MaxResult, opt => opt.Ignore());

		CreateMap<ContactQuery, ContactInfo>()
			.ForMember(dest => dest.SomeProperty, opt => opt.Ignore());
	}

	protected override string ProfileName
	{
		get { return "Sync"; }
	}
}

Now then a test to prove that my mappings are actually valid. I like to clear everything both before the test and after the test to be sure I only validate the necessary mappings:

[TestFixture]
public class Verify_AutoMapper_mappings_for
{
	[Test]
	public void ContactProfile()
	{
		Mapper.Reset();
		Mapper.AddProfile<ContactProfile>();
		Mapper.AssertConfigurationIsValid();
		Mapper.Reset();
	}
}

Tags: , ,

C# | Testing | AutoMapper

Collecting all constant values of a class using reflection

by Mikael Henriksson 6. February 2010 16:43

I sometimes have to test outgoing messages before we send them out. Since we work in the mobile industry we need to communicate with mobile phones and alas most countries have legal requirements that we much abide to. Because of some Swedish and Norwegian characters like “åäö” message length etc varies. So before we change any messages in production everyone get’s to have a say on the message. To do this I have created a simple unit test where I send the SMS to everyone. though there are quite a few places where this is done and I got bored with copy pasting the numbers or using them one and one to concatenate them so I decided to use reflection for this instead.

 

public class Numbers
{
	public const string EmployeeOne = "xxxxxxxxxx";
	public const string EmployeeTwo = "xxxxxxxxxx";
	public const string EmployeeThree = "xxxxxxxxxx";
	public const string EmployeeFour = "xxxxxxxxxx";
	public const string EmployeeFive = "xxxxxxxxxx";
	public const string EmployeeSix = "xxxxxxxxxx";
	public const string EmployeeSeven = "xxxxxxxxxx";
	public const string EmployeeEight = "xxxxxxxxxx";
	public const string EmployeeNine = "xxxxxxxxxx";
	public const string EmployeeTen = "xxxxxxxxxx";
	public const string EmployeeEleven = "xxxxxxxxxx";
	public const string EmployeeTwelve = "xxxxxxxxxx";
}

 

Wow a class of constants… how beautiful! ;) Well they have their place and this is the place for a class of constants.

Instead of manually handling all the numbers when I need them all is to make good use of reflection. Most reflection starts with a type somehow and in this case we are looking for fields that are public, static and flatten hierarchy means that inherited classes protected and public constants should be searched as well.

We want the Literal (compiled values) and they should not be init (initialized using constructor only).

private const BindingFlags Flags = BindingFlags.Public | 
            BindingFlags.Static | BindingFlags.FlattenHierarchy;

public IEnumerable<string> GetAllNumbers(Type type)
{
	FieldInfo[] fieldInfos = type.GetFields(Flags);
	return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly)
		.Select(fi => fi.GetValue(null).ToString());
}

If I wanted to create a tool / library around this I would probably wrap the above method with some generic.

public IEnumerable<string> GetAllNumbers<T>()
{
	return GetAllNumbers(typeof (T));
}

Tags: ,

C# | Reflection

NHibernate – A waste of precious time!!

by Mikael Henriksson 8. May 2009 00:13

By coincidence I found something called Fluent NHibernatewow automatic mappings etc. After having a read up on NHibernateI also found something called NHibernate Validationthat seemed to serve my purposes well. The only reason for having a look around was to easily add validation to my classes. I found this a bit hard with Entity Framework.

All was well and good I started of with learning how to map entities with fluent. After a couple of hours I had produced a model that I could use. Then I hooked in the validation dll. Tried to validate one of my entities with an attribute. I am not very fond of the whole xml configuration thing to tell you the truth. It’s more work in the long run. I find that if I need to change something I am changing at code level so I might as well change the attribute! Anyway, when I try to instantiate the ValidatorEngie with:

var validator = new ValidatorEngine();

It throws an exception! It’s due to using an older version of NHibernate so I try to upgrade NHibernate and then everything else starts throwing exceptions or complaining about versions. I added some version remaps in the app.config but not luck.

That’s the time I stop trying and go back to Entity Framework. Even if it is still a bit immature it is a piece of cake setting up even for more advanced scenarios and with version 2 on the way I rather have to quickly create a validation service than have to get in to every single detail about NHibernate!

Thanks but no thanks!

Tags: ,

C# | NHibernate

Thread safe .NET wurfl API (hopefully)

by Mikael Henriksson 29. April 2009 11:32

First of all I’d like to thank Luca Passani for all his great work with wurfl. He’s done a good job. The database is updated at least once a month and the community I suppose has done a good job with the different API’s. I can however not use the .NET API’s. They are not thread safe and I thought I’d share with you how I improved the old .NET API.

I started of with renaming pretty much everything since the names did not talk to me. We can call the in-memory wurfldatabase the WurflHolder and the class to navigate the holder is called WurflNavigator. The implementation details was changed a bit as well since it did not give me the correct results but that is a different blog post. Why did I make it a Singleton class you ask? Well I tried with a static class first but it caused issues all over the planet. I had people getting detected with a completely wrong mobile phone. Also the logs was totally not right. It basically cause the whole application to go wrong. I though maybe this is a good place for the Singleton pattern and I was right. I am not sure my Singleton implementation is completely thread safe but it is sufficient for now.

Let’s start with the Singleton instance:

public class Singleton<T> where T : class, new()
{
    protected Singleton()
    {

    }

    public static T Instance
    {
        get { return SingletonCreator<T>.CreatorInstance; }
    }

    private sealed class SingletonCreator<S> where S : class, new()
    {
        private static readonly S instance = new S();

        public static S CreatorInstance
        {
            get { return instance; }
        }
    }
}

I think the code speaks for itself. Basically if it’s not created I create it at runtime. The reason for making it generic is that I actually use it for two more classes. Then the API needs a couple of modifications, first in Global.asax we need to load the xml document:

Singleton<WurflHolder>.Instance.Load(HostingEnvironment.MapPath("/wurfl.xml"));

Then the default constructor for the Navigator needs to be changed to something like:

public WurlfNavigator()
{
    try
    {
        _wurflFileNotLoaded = Singleton<WurflHolder>.Instance.WurflFileNotLoaded;
        if (!Singleton<WurflHolder>.Instance.IsLoaded)
        {
            ThrowException(_wurflFileNotLoaded + ". Be sure that the creation of _wurfl have ran OK",
                           "prepareNavigatorModel");
        }
        _userAgentAndId = Singleton<WurflHolder>.Instance.UserAgentAndId;
        _idAndFallback = Singleton<WurflHolder>.Instance.IdAndFallback;
        _idAndCapabilities = Singleton<WurflHolder>.Instance.IdAndCapabilities;
        _capabilityNames = Singleton<WurflHolder>.Instance.CapabilityNames;
    }
    catch (Exception ex)
    {
        ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        log.Error("WurlfNavigator", ex.InnerException);
    }
}

This totally stops all randomness in the entire website. maybe not the best thing to keep it in memory but hey, it’s better than total unreliability!

Tags: ,

C# | wurfl

An extension approach to get the name of a month

by Mikael Henriksson 17. February 2009 21:15

I am as I have posted before lazy. I am also very hesitant to using extension methods. There is nothing wrong with this but they pop up everywhere eventually. I did however create an extremely useful extension method for something as simple as getting the name of a specified dates month. This is displayed based on the current culture.

It is only one line of code but it’s one hell of a line to remember and that’s why I created the extension for it! :)

using System.Threading;
public static string MonthName(this DateTime date)
{
	return Thread
	.CurrentThread
	.CurrentCulture
	.DateTimeFormat
	.MonthNames[date.Month];
}

Tags:

C#

Using the registry in .NET

by Mikael Henriksson 27. January 2009 18:59

Today I learned something new and I kind of like it so I thought I'd share my expertise! :)

We have four instances of the same application running on the same machine but all with different configurations (all are executed from thin clients via RDP). Now that's nothing new, nothing strange it's just the way it is. I need to add another thin client and the new one need to need to share some configuration with one of the others. They both need to have full control over our packages configuration. Never mind what types of packages since that is utterly unimportant. I was looking at reading / writing external config files but realized I'd just get lost somewhere. That is why I decided to have a look at the registry!

First I need to create something to "hold" my registry data to make it reusable throughout my application. Oh you could indeed have it all stored in a config file but I'd like to minimize the risk of someone changing / deleting the file by mistake. And since this registry class isnt subject of change for a long time I chose this approach instead but feel free to pick your own choice.

using Microsoft.Win32;
namespace Company.Application.RegistryConfiguration
{
    public class RegistryKeys
    {
        public static RegistryKey PackageSettings()
        {
            RegistryKey keyPS = Registry.LocalMachine
                .CreateSubKey("Software")
                .CreateSubKey("Company\\Package Settings"); return keyPS;
        }
    } 
    public class RegistryValues
    {
        public const string ValueQuantityInHistory = "QuantityInHistory";
        public const string ValueQuantityInDatabase = "QuantityInDataBase";
        public const string ValuePackageNumber = "PackageNumber";
        public const string ValueMinPackageNumber = "MinPackageNumber";
        public const string ValueMaxPackageNumber = "MaxPackageNumber";
    }
}

Next thing is reading and writing to the registry based on my 2 Registry Classes. It's all a walk in the park really. From here on it's even more straight forward, I'll just let you have a look at the full Packages class. Don't forget to reference Microsoft.Win32 with a simple using.

public class Packages    
{        
	private RegistryKey keyPS = RegistryKeys.PackageSettings();         
	private int _quantityInDb;        
	private int _quantityInHistory;         
	private int _packageNr;        
	private int _minPackageNr;        
	private int _maxPackageNr;     

	/// <summary>        
	/// Loads the package configuration from the registry        
	/// </summary>       
	/// <remarks>       
	/// Checks for null values, if we have a null value something messed up and we load the config from backup file       
	/// </remarks>        
	private void LoadPackageSettings()        
	{             
		_quantityInDb = Convert.ToInt32(keyPS.GetValue(RegistryValues.ValueQuantityInDatabase));           
		_quantityInHistory = Convert.ToInt32(keyPS.GetValue(RegistryValues.ValueQuantityInHistory));            
		_packageNr = Convert.ToInt32(keyPS.GetValue(RegistryValues.ValuePackageNumber));            
		_minPackageNr = Convert.ToInt32(keyPS.GetValue(RegistryValues.ValueMinPackageNumber));            
		_maxPackageNr = Convert.ToInt32(keyPS.GetValue(RegistryValues.ValueMaxPackageNumber));             
		
		if (_quantityInDb.Equals(0) && _quantityInHistory.Equals(0) && _minPackageNr.Equals(0))            
		{                
			LoadBackedUpPackageSettings();            
		}         
	}         
	
	/// <summary>
	/// Saves the package configuration in the registry       
	/// </summary>        
	/// <remarks>        
	/// Lastly we backup all settings to let's say a text file.        
	/// </remarks>        
	private void SavePackageSettings()        
	{            
		try
		{
			keyPS.SetValue(RegistryValues.ValueQuantityInDatabase, _quantityInDb, RegistryValueKind.DWord);                
			keyPS.SetValue(RegistryValues.ValueQuantityInHistory, _quantityInHistory, RegistryValueKind.DWord);                 
			keyPS.SetValue(RegistryValues.ValuePackageNumber, _packageNr, RegistryValueKind.DWord);                
			keyPS.SetValue(RegistryValues.ValueMinPackageNumber, _minPackageNr, RegistryValueKind.DWord);                
			keyPS.SetValue(RegistryValues.ValueMaxPackageNumber, _maxPackageNr, RegistryValueKind.DWord);
		}            
		catch (Exception ex)            
		{                
			// Logger.LogException(ex);                
			// Tell the user something went wrong            
		}            
		finally
		{
			BackupPackageSettings();
		}       
	}    
}

There are several more ways to use the registry, this is just one of them, maybe not even a good way. Don't look at me, I'm not an expert! Now go hack that registry of yours :)

Tags: ,

C# | Windows Forms

Handle enums gracefully...

by Mikael Henriksson 27. January 2009 18:59

As of lately I have been trying to use Enums wherever I can. In the project I just finished I had a bunch of buggy code and wanted more detailed descriptions so I began writing a grand class to handle all the exceptions. I decided to use and Enum for describing all the different logging routines but when I needed to actually do something based on more than one LogType I ran in to patrol. I wanted to iterate through the declared enum type for the class and log to all types in a sweep. First I got kind of confused looking at my code and since I wanted the others in the team to be able to use it easily I had to rethink. First thing was to refactor the class to something really simple, then begin adding my "normalized" methods but I got stuck at not being able to gracefully looping through my enum.

Let's say I have an enum like this:

[Flags()]   
public enum LogType   
{
	/// <summary>Exception will be logged to a text file</summary>  
	TextFile = 1,
	/// <summary>Exception will be logged to the application event log</summary>
	EventLog = 2,
	/// <summary>Exception will be logged via email</summary>
	Email = 4,
	/// <summary>Log to a website</summary>
	WebSite = 8,
	/// <summary>Log to a database of your choice</summary>
	Database = 16,
	/// <summary>A summary of all LogSources</summary
	All = TextFile | EventLog | Email | WebSite
}

The above code should be a no-brainer, if you don't understand it you should probably buy a book! :)

I found a great tool for making my work extremely readable for my college by the way, it's called Ghost Doc and if you have not tried it yet I urge you to! The project can be found over at http://www.roland-weigelt.de/ghostdoc/
If you are stronger in the code than my granny was I believe you should have no problems reading the following code either, and making good use of it!

public static Collection<T> ConvertEnumValuesToCollection<T>(T enumInputValue) 
where T : struct { Type enumType = typeof(T); if (!enumType.IsEnum) { throw new ArgumentException(enumType.ToString()
+ " Is not an Enum.", "T", null); } Array enumValues = Enum.GetValues(enumType); long inputValueLong = Convert.ToInt64(enumInputValue); Collection<T> enumOutputCollection = new Collection<T>(); foreach (T enumValue in enumValues) { long enumValueLong = Convert.ToInt64(enumValue); if ((enumValueLong&inputValueLong)==enumValueLong&&enumValueLong!=0) { enumOutputCollection.Add(enumValue); } } if (enumOutputCollection.Count == 0 && enumValues.GetLength(0) > 1) { T enumNoneValue = (T)enumValues.GetValue(0); if (Convert.ToInt64(enumNoneValue) == 0) { enumOutputCollection.Add(enumNoneValue); } } return enumOutputCollection; }

No credits at all to me for this one though, I stumbled upon it by mistake but since having computer crashes etc I cannot give my credits to the right webby unfortunately. Hugs and kisses for this though and definately worth sharing. If you have any questions what so ever about enums, flags or .net the myth the legend and the framework I recommend contacting Krzystof Cwalina. One of the chief architects over at Microsoft. If you just want to learn some more about enums I recommend you checking his extremely extensive do's and don'ts out over at http://blogs.msdn.com/kcwalina/archive/2004/05/18/134208.aspx

Tags:

C#

Printing PDF’s programmatically from C#

by Mikael Henriksson 27. January 2009 18:59

This turns out to be a royal pain in the a$$. Actually I do not want to print a pdf, I want to print from SQL Reporting Services Express and it CAN be done but the result is less than flattering. After hours of searching and only finding libraries I either have to pay for or spend too much time configuring I finally found a pdf reader that takes a file AND printer as arguments and redirects to that specified printer without even opening a “window” (yes I am using windows :) ). Don’t even think about using Adobe ever! If you are using Adobe please uninstall that piece of crap and install the free version of Foxit PDF Reader. It is completely free of charge and let’s you print pdf’s from the the command prompt.

Anyway, the code to generate the pdf from C# isn’t all that hard to find on the internet. The code to send that piece of work to the printer probably is not common knowledge though so I’ll at least share that with you! First generate your report, get the stream then write that SQL Report stream to a PDF and get the full path to the file and this is all you have to do.

proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.FileName = @"C:\Program Files\Foxit Software\Foxit Reader\FoxitReader.exe";
string Arguments = String.Format(@"{0} /t {1}", @"C:\Full Path To\File.pdf", @"\\Server\Printer");
proc.StartInfo.Arguments = Arguments;

proc.Start();

Compare that to Adobe’s Print Verb. If you are using Adobe you have to wait for dull/slow/boring/not working-exit. I found that when I used the proc.StartInfo.Verb = “Print” from adobe it would not print all of the times I used it. Further more it would give me file system errors etc until I restarted the server because Adobe Reader would actually lock the file and keep that lock. So if something went wrong with the print a new file could not overwrite the existing one until restart.

This new approach is just beautiful, fast & furious!

Have fun printing those documents of yours :)

Tags:

C#

About the author

Life architect specialized in programming