Label Cloud

Showing posts with label c# sample source. Show all posts
Showing posts with label c# sample source. Show all posts

Monday, June 29, 2009

Oracle Coherence SIG - Presentation and sample code

Oracle Coherence SIG came around. Excellent event. It was twitted live, so go ahead and search : http://search.twitter.com/search?q=%23nycsig

I spend around 30 minutes talking about using Coherence with .Net. First part of the presentation was on general Coherence configuration and setup. Second part was on more advanced use of serialization wrappers and LINQ to Coherence. People asked some great questions. I guess the one important point I wanted to reiterate is that you should not be afraid of using Coherence with .Net. It is a really great product. You will definitely get a lot of benefit out of the box with it. The wrappers I’ve provided will create some overhead, but that should be acceptable to many. Wrappers and LINQ are not a solution that will solve every .Net developers problem, but should definitely get you started.

I’ve uploaded the presentation and the samples (in 4MB large zip file).

Presentation http://tfanshteyn.110mb.com/CoherencePresentation.pdf
Sample Files http://tfanshteyn.110mb.com/CoherencePresentation.zip


Share/Save/Bookmark

Tuesday, April 07, 2009

Linq to Coherence + Attributes + MetaData = Cool

First, we got the Linq Provider for Oracle Coherence

Second, we got attribute based serialization

Third, Metadata in the serialization stream

Once we put all that together, what we get is a set of very clean way of storing and querying data in Oracle Coherence.

A Coherence Linq provider now supports passing a CoherenceQueryTranslator as a parameter. I am providing a MetadataCoherenceQueryTranslator that uses getProperty method to access property originally serialized by the Generic Serializer. Here’s almost all relative .Net Code:

Person Class

[POFSerializableObject(StoreMetadata=true)]
    public class Person// : IPortableObject
    {
        [POFSerializableMember(Order=0,WriteAsType=POFWriteAsTypeEnum.Int16)]
        public int ID { get; set; }
        [POFSerializableMember(Order=1)]
        public string FirstName { get; set; }
        [POFSerializableMember(Order = 2)]
        public string LastName { get; set; }
        [POFSerializableMember(Order = 3)]
        public string Address { get; set; }
        [POFSerializableMember(Order = 4)]
        public string Title { get; set; }
        public Person()
        {
        }
    }
Add object function
INamedCache cache = CacheFactory.GetCache("dist-Person");
for (int i = 0; i < 1000; i++)
{
cache.Add(i, new Person()
{
   ID = i,
   FirstName = string.Format("First Name {0}", i),
   LastName = string.Format("LastName {0}", i),
   Address = string.Format("Address {0}" , Guid.NewGuid()) ,
   Title = i % 2  == 1 ? "Mr" : "Mrs"
   });
}

Query using Linq Query:

CoherenceQuery<Person> coherenceData =
 new CoherenceQuery<Person>(
	 new CoherenceQueryProvider(CacheFactory.GetCache("dist-Person"), 
		 new MetadataCoherenceQueryTranslator()));
string likeClause = "%8";
var people = from person in coherenceData
			 where
				(person.FirstName.Like("Test")
				 || person.LastName.Like(likeClause))
				 && person.Title == "Mrs"
			 select new { person.Title, person.ID, person.LastName };
IFilter filter = ((ICoherenceQueryable)people).Filter;
dataGridView1.DataSource = people.ToArray();

Internally, MetadataCoherenceQueryTranslator, will convert the linq query into a filter and execute the query against the Java POFGenericObject


Share/Save/Bookmark

Monday, April 06, 2009

Attributes based Coherence POF Serializer

As much as I love Oracle Coherence, it is a very much a Java product. .Net has lots of cool tricks that can be used during programming, however, they are not implemented in Coherence .Net

Many .Net developers are used to using attributes to define object serialization. A Coherence Generic serializer allows a developer to specify POF Serialization using object attributes as well. Here’s a simple example

    [POFSerializableObject()]
    public class Person// : IPortableObject
    {
        [POFSerializableMember(Order=0,WriteAsType=POFWriteAsTypeEnum.Int16)]
        public int ID { get; set; }
        [POFSerializableMember(Order=1)]
        public string FirstName { get; set; }
        [POFSerializableMember(Order = 2)]
        public string LastName { get; set; }
        [POFSerializableMember(Order = 3)]
        public string Address { get; set; }
        [POFSerializableMember(Order = 4)]
        public string Title { get; set; }
        public Person()
        {
        }
        //#region IPortableObject Members
        //public void ReadExternal(IPofReader reader)
        //{
        //    ID = reader.ReadInt32(0);
        //    FirstName = reader.ReadString(1);
        //    LastName = reader.ReadString(2);
        //    Address = reader.ReadString(3);
        //    Title = reader.ReadString(4);
        //}
        //public void WriteExternal(IPofWriter writer)
        //{
        //    writer.WriteInt32(0, ID);
        //    writer.WriteString(1, FirstName);
        //    writer.WriteString(2, LastName);
        //    writer.WriteString(3, Address);
        //    writer.WriteString(4, Title);
        //}
        //#endregion
    }

To force Coherence to use the serialization attribute, use the the Coherence Generic Serializer in the POF Configuration File

    <user-type>
      <type-id>1001</type-id>
      <class-name>CoherenceSample.Person, CoherenceSample</class-name>
      <serializer>
        <class-name>Coherence.Contrib.POFGenericSerializer, Coherence.Contrib</class-name>
      </serializer>
    </user-type>

Some important points to mention:

  • Object must include POFSerializableObject attribute
  • Generic Serializer will serialize Properties and Members
  • Objects MUST NOT implement IPortableObject. Doing so will force Coherence to use ReadExternal and WriteExternal functions instead of a custom serializer
  • Order parameter of the attribute is optional. The order of serialization is Order, Alphabetical Ascending. Meaning. Multiple attributes can have the same order argument, and will be serialized in alphabetical order
  • WriteAsType parameter is optional and is usually derived based on the source type.
  • Serializer uses Converter.Convert() to convert between object types.
  • Most (but not all) main types are implemented. Check out the source code for specifics
  • Hardcoding a serializer still provides better performance due to extra boxing and object conversion performed by the serializer.

Please check out the latest code on Google Code: http://code.google.com/p/linqtocoherence

Technorati Tags: ,,,


Share/Save/Bookmark

Tuesday, July 15, 2008

Simple EntityMapperTranslator

CAB defines a very clean way of defining a translator for converting objects from one type to another. A typical use for this is to convert from an "wire" object a business object and back. This is done by creating an EntityMapperTranslator. Defining two methods: BusinessToService and ServiceToBusiness. And then registering the translator in the IEntityTranslatorService.

Often, the business object identical or nearly identical to the wire object. The code below will copy every property from the SourceType to the TargetType. Of course, this can be used outside of the EntityMapperTranslator as well for the same purpose.

class TypeTranslator : EntityMapperTranslator<SourceType, TargetType>
{
    protected override TargetType BusinessToService(IEntityTranslatorService service, SourceType value)
    {
        TargetType target = new TargetType();
        foreach (PropertyInfo pi in value.GetType().GetProperties())
        {
           PropertyInfo newPi = target.GetType().GetProperty(pi.Name);
           if (newPi != null)
           {
              if (service.CanTranslate(newPi.PropertyType, pi.PropertyType))
                 newPi.SetValue(target, service.Translate(newPi.PropertyType, pi.GetValue(value, null)), null);
              else
                 newPi.SetValue(target, pi.GetValue(value, null), null);
           }
        }
        return target;
    }
}


Share/Save/Bookmark

Thursday, January 10, 2008

Customizing SCSF Guidance Package for Modular Development

One of the requests that I've received from other developers is the ability to use SCSF for developing a module without including the shell in the solution. We develop a large number of modules independently in different groups and having the shell be a part of every module was getting to be a problem.

The only issue that I was getting with getting this to work was that SCSF guidance package would fail in ViewTemplateCS when I would right click on a folder and tried to add a new view to the project.

To solve the issue, I was made a small tweak to the source in the ViewTemplateReferenceCS.cs. (The code comes with the SCSF, however, you will have to install it separately after the SCSF is installed) The culprit is the function ContainsRequiredReferences(Project project) Specifically the call to ContainsReference(project, prjCommon.Name)

Since the common project is not in the solution, the call failed with Null Reference exception. All I had to do was to change the last line of the function to be ContainsReference(project, "Infrastructure.Interface"); Then recompile the GuidancePackage solution and place the Microsoft.Practices.SmartClientFactory.GuidancePackage.dll into the C:\Program Files\Microsoft Smart Client Factory\Guidance Package folder.


Share/Save/Bookmark

Friday, December 21, 2007

Closing and Terminating views in SCSF

There is a bug in the SCSF implementation that views even though closed, will not terminate the workitems. This was described on codeplex in the following thread: http://www.codeplex.com/smartclient/Thread/View.aspx?ThreadId=4370

Here is my workaround:

In the View.Designer.cs

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_presenter != null)
{
_presenter.OnCloseView(); // <<<<<<<<======= Workaround
_presenter.Dispose();
}

if (components != null)
components.Dispose();
}

base.Dispose(disposing);
}

In the ViewPresenter.cs

bool Closing = false;
public void OnCloseView()
{
if (!Closing)
{
Closing = true;
if (WorkItem.Status != WorkItemStatus.Terminated)
WorkItem.Terminate();

base.CloseView();
}
}

The code will force the workitem to be terminated after the view is closed

Technorati Tags: , ,


Share/Save/Bookmark

Thursday, December 20, 2007

Compiling .NET 3.5 code to .NET 2.0 Works

This is something that is totally cool. You can use Visual Studio 2008 and a lot of the new functionality and cross compile it to .NET 2.0 and run it on the older framework. For Example, You can use Var objects, Simple Property Declarations, Property Constructors, Lambda expressions

Here's an example program that can be compiled with VS 2008 to the .NET 2.0 framework

static class Program
    {
        private class Client
        {
            public string Name { get; set; }
            public string Address { get; set; }
        }

        private static List<Client> clients = new List<Client>
        {
            new Client() {Name = "Name1", Address = "Address1" },
            new Client() {Name = "Name2", Address = "Address2" },
            new Client() {Name = "Name3", Address = "Address3" },
            new Client() {Name = "Name13", Address = "Address13" },
            new Client() {Name = "Name123", Address = "Address123" }
        };

        [STAThread]
        static void Main()
        {
            List<Client> ClientsWith1 = clients.FindAll(c => c.Name.Contains("1"));
            ClientsWith1.ForEach(c =>
            {
                var NewClient = new
                {
                    Name = c.Name,
                    Address = c.Address
                };
                Console.WriteLine(NewClient.ToString());
            });
            Console.ReadKey();
        }
    }
 
Here's the output

{ Name = Name1, Address = Address1 }
{ Name = Name13, Address = Address13 }
{ Name = Name123, Address = Address123 }

And it works without .NET 3.5 installed.

For those interested, Here's a Reflected code

internal static class Program
{
    // Fields
    private static List<Client> clients;
    [CompilerGenerated]
    private static Predicate<Client> CS$<>9__CachedAnonymousMethodDelegate2;
    [CompilerGenerated]
    private static Action<Client> CS$<>9__CachedAnonymousMethodDelegate3;

    // Methods
    static Program()
    {
        List<Client> <>g__initLocal4 = new List<Client>();
        Client <>g__initLocal5 = new Client();
        <>g__initLocal5.Name = "Name1";
        <>g__initLocal5.Address = "Address1";
        <>g__initLocal4.Add(<>g__initLocal5);
        Client <>g__initLocal6 = new Client();
        <>g__initLocal6.Name = "Name2";
        <>g__initLocal6.Address = "Address2";
        <>g__initLocal4.Add(<>g__initLocal6);
        Client <>g__initLocal7 = new Client();
        <>g__initLocal7.Name = "Name3";
        <>g__initLocal7.Address = "Address3";
        <>g__initLocal4.Add(<>g__initLocal7);
        Client <>g__initLocal8 = new Client();
        <>g__initLocal8.Name = "Name13";
        <>g__initLocal8.Address = "Address13";
        <>g__initLocal4.Add(<>g__initLocal8);
        Client <>g__initLocal9 = new Client();
        <>g__initLocal9.Name = "Name123";
        <>g__initLocal9.Address = "Address123";
        <>g__initLocal4.Add(<>g__initLocal9);
        clients = <>g__initLocal4;
    }

    [STAThread]
    private static void Main()
    {
        if (CS$<>9__CachedAnonymousMethodDelegate2 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate2 = delegate (Client c) {
                return c.Name.Contains("1");
            };
        }
        if (CS$<>9__CachedAnonymousMethodDelegate3 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate3 = delegate (Client c) {
                Console.WriteLine(new { Name = c.Name, Address = c.Address }.ToString());
            };
        }
        clients.FindAll(CS$<>9__CachedAnonymousMethodDelegate2).ForEach(CS$<>9__CachedAnonymousMethodDelegate3);
        Console.ReadKey();
    }

    // Nested Types
    private class Client
    {
        // Fields
        [CompilerGenerated]
        private string <Address>k__BackingField;
        [CompilerGenerated]
        private string <Name>k__BackingField;

        // Properties
        public string Address
        {
            [CompilerGenerated]
            get
            {
                return this.<Address>k__BackingField;
            }
            [CompilerGenerated]
            set
            {
                this.<Address>k__BackingField = value;
            }
        }

        public string Name
        {
            [CompilerGenerated]
            get
            {
                return this.<Name>k__BackingField;
            }
            [CompilerGenerated]
            set
            {
                this.<Name>k__BackingField = value;
            }
        }
    }
}
 
Technorati Tags: ,,,,


Share/Save/Bookmark

Calculating Hashvalues for files the way .NET does for the Application Manifest

hey

I had to write a custom download component to download modules for a ClickOnce deployed application. The actual downloading is simple, the tricky part was creating the manifest and make sure that I only download files that are required.

I am using an GeneraApplicationManifest MSBuild task to generate an application manifest. The documentation is very easy to follow.

The generated manifest will include a Hash value. It is fairly simple to compute the same hash value manually and be able to validate it.

private bool HashChanged(string fileName, string originalHashValue)
        {
            byte[] Hash = Convert.FromBase64String(originalHashValue));
            byte[] newHash;
            SHA1Managed sha = new SHA1Managed();
         
            FileStream strm = null;
            try
            {
                strm = new FileStream(fileName, FileMode.Open, FileAccess.Read);         
                newHash = sha.ComputeHash(strm);
            }
            finally
            {
                if (strm != null)
                    strm.Close();
            }

            if (Hash.Length != newHash.Length)
                return true;
            for(int i = 0; i< Hash.Length; i++)
            {
                if (Hash[i] != newHash[i])
                    return true;
            }
            return false;
        }
Technorati Tags: ,,,


Share/Save/Bookmark

Wednesday, December 05, 2007

Creating a ClickOnce Smart Client CAB Based (SCSF) application with Environment Overrides

My application is distributed via ClickOnce and a requirement is to be able to provide endpoint overrides for multiple environments. Here's what was done to create the solution


Smart Client Software Factory includes a service called EndpointCatalog. It allow for easy management of endpoints with environment overrides. Start by adding a Microsoft.Practices.SmartClient.EndpointCatalog.dll as a reference to Infrastructure.Module. Then open ModuleController class in and register the EndpointCatalog service.

private void AddServices()
{
IEndpointCatalog catalog = WorkItem.RootWorkItem.Services.Get<IEndpointCatalog>(false);
if (catalog == null)
{
IEndpointCatalogFactory catalogFactory =
new EndpointCatalogFactory("Endpoints");

catalog = catalogFactory.CreateCatalog();
WorkItem.RootWorkItem.Services.Add<IEndpointCatalog>(catalog);
}

This will read the endpoint catalog from the application.config file , section Endpoints. Here is a partial app.config

<configSections>
<section name="Endpoints" type="Microsoft.Practices.SmartClient.EndpointCatalog.Configuration.EndpointSection, Microsoft.Practices.SmartClient.EndpointCatalog" />
</configSections>

<Endpoints>
<EndpointItems>
<add Name="DataService.DataClient"
Address="http://server/DataService.svc"
UserName="default-user-name" Password="default-password" Domain="default-domain">
<NetworkItems>
<add Name="QA" Address="http://qa.server/DataService.svc"/>
<add Name="UAT" Address="http://uat.server/DataService.svc"/>
</NetworkItems>
</add>
</EndpointItems>
</Endpoints>

The endpoints section defines an endpoint, and an override for each environment. The catalog will return the override if it exists, or the original entry if it does not.

To create the WCF client, I created the following function.

T CreateWCFClient<T, Ti>()
where T : ClientBase<Ti>, new()
where Ti : class
{
try
{
T client = new T();
if (endpointCatalog.EndpointExists(typeof(T).FullName))
{
client.Endpoint.Address = new EndpointAddress
(endpointCatalog.GetAddressForEndpoint(typeof(T).FullName, Environment));
}
return client;
}
catch (Exception)
{
throw;
}
}

Add the required service to the module that will hold the function and you are almost done. The request to create the client is as follows.

_DataWebService = CreateWCFClient<DataService.DataClient, DataService.IDataClient>();

Please comment for any questions, I'll try to clarify


Share/Save/Bookmark

Saturday, October 13, 2007

Providing multiple endpoints for the WCF service

I had to implement compression for an internal WCF service. A requirement however is to make sure that older version of the service is left as is. To achieve that we've added another endpoint to the an existing binding

Here's the original Configuration File for the server.

<services> <service name="Repositories.Clients" behaviorConfiguration="DebugBehavior"> <endpoint name="Clients" contract="IClients" binding="basicHttpBinding" /> </service> <services> <behaviors> <serviceBehaviors> <behavior name="DebugBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors>

The URL for the service is http://hostname/Clients.svc

Here's the new file

<services> <service name="Repositories.Clients" behaviorConfiguration="DebugBehavior"> <endpoint name="Clients" contract="IClients" binding="basicHttpBinding" /> <endpoint name="ClientsCompressed" contract="IClients" bindingConfiguration="compressedConfiguration" binding="customBinding" address="compressed"/> </service> <services> <bindings> <customBinding> <binding name="compressedConfiguration"> <compression compressionMode="GZip" compressionLevel="Normal"/> <httpTransport/> </binding> </customBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="DebugBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors>

The new URL for the service is http://hostname/Clients.svcc/compressed

The old service works just like before. But the new one requires compressed data. It is serviced by a completely separate binding and has independent configuration.

 


You can follow the same above example to specify multiple client endpoints as well. However, if multiple endpoints exist, the WCF Proxy has to be created by specifying an endpoint name.

 

localhost.Clients1Client client = new localhost.Clients1Client("compressed");

Technorati Tags: , ,


Share/Save/Bookmark

Friday, August 17, 2007

Tweaking RuleSetDialog to Resize

I've started working with Windows Workflow Rulesets to apply dynamically apply business rules to some internal processes. This involves using External RuleSet Toolset from Microsoft samples (some more on that in a later post)

One of the biggest peeves about the RuleSetDialog is its inability to resize. Viewing a complex rule in a three line window is very uncomfortable. It is, however, pretty easy to tweak the dialog and make it a lot more user friendly.

External RuleSet editor comes in source code. Open the code using Visual Studio, open the code for the RuleSetEditor form and find the editButton_Click event. What you'll may notice is that the RuleSetDialog class used derives from Dialog. Add a new function that will adjust the dialog to make it resizable.

private void TweakRuleSetDialogToResizable(RuleSetDialog ruleSetDialog) { ruleSetDialog.FormBorderStyle = FormBorderStyle.Sizable; ruleSetDialog.HelpButton = false; ruleSetDialog.MaximizeBox = true; ruleSetDialog.Controls["okCancelTableLayoutPanel"].Anchor = AnchorStyles.Right | AnchorStyles.Bottom; ruleSetDialog.Controls["rulesGroupBox"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; ruleSetDialog.Controls["ruleGroupBox"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; ruleSetDialog.Controls["ruleGroupBox"].Controls["thenTextBox"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; ruleSetDialog.Controls["ruleGroupBox"].Controls["elseLabel"].Anchor = AnchorStyles.Left | AnchorStyles.Bottom; ruleSetDialog.Controls["ruleGroupBox"].Controls["elseTextBox"].Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom; ruleSetDialog.Controls["ruleGroupBox"].Controls["conditionTextBox"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Controls["chainingBehaviourComboBox"].Anchor = AnchorStyles.Top | AnchorStyles.Right; ruleSetDialog.Controls["rulesGroupBox"].Controls["panel1"].Controls["chainingLabel"].Anchor = AnchorStyles.Top | AnchorStyles.Right; }

Then call the function passing the RuleSetDialog before it is display from the editButton_Click event.

RuleSetDialog ruleSetDialog = new RuleSetDialog(selectedRuleSetData.Activity, null, selectedRuleSetData.RuleSet); //Tweak the RuleSetDialog TweakRuleSetDialogToResizable(ruleSetDialog); DialogResult result = ruleSetDialog.ShowDialog();


Share/Save/Bookmark

Thursday, June 14, 2007

Alternatives to Enum(s)

I had the need to provide an enumerated value to in my code, and be able to easily convert the value to a string representation. .NET Enum can only be numeric and did not provide the functionality needed. A coworker showed me very nice way to create the same functionality with a simple class

//Using this instead of an ENum - beeing fancy internal sealed class ActionType { private readonly string _action; private ActionType(string action) { this._action = action; } public override string ToString() { return _action; } public static readonly ActionType Update = new ActionType("U"); public static readonly ActionType Delete = new ActionType("D"); }

What the class allows me to do is the following:

main() { ActionType action = ActionType.Update; action = ActionType.Delete; string sAction = action.ToString(); }

sAction will have "D" as the string representation for the Delete action.


Share/Save/Bookmark

Thursday, June 07, 2007

Making your assemblies describe themselves

One of the biggest hurdles of the release process is to make sure you know exactly what you are releasing. To make that job a little easier, I've modified my project files to generate and include build related information in the assembly properties.

I am using a great open source project MSBuild Community Tasks.

Three of the tasks included are Time, Version and AssemblyInfo. Here's the process to incorporate them into the project.

Add a new project to the visual studio solution. I made this a C# project to make it easier to integrate with Visual Studio. Then open the project in your favorite text editor. Scroll down to the <Target Name="Build"> line. Now make the contents of the target the following

<Time Format="yyyy/MM/dd HH:mm:ss">
<Output TaskParameter="FormattedTime" PropertyName="buildDate" />
</Time>
<AssemblyInfo CodeLanguage="CS" OutputFile="GlobalInfo.cs"
AssemblyDescription="Build Date: $(buildDate)
Configuration: $(Configuration)$(Platform)" />
<Version VersionFile="version.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyVersion.cs" AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />

Now add a new text file to the project called version.txt and edit to have a single line "1.0.0.0"

That is almost it. After compiling the above project, you will receive two new files: GlobalInfo.cs and AssemblyVersion.cs. GlobalInfo.cs will contain the BuildDate and Configuration used during compilation. AssemblyVersion.cs will contain the version information based on the version.txt file. See help for AssemblyVersion task for how to make it increment the version number during the build.

Another task is to add the two new files as a replacement to the AssemblyInfo.cs that's usually a part of every solution. I do it with a text editor to make sure that they point to the file outside of the local project but rather to the newly generated files. That makes files read-only. The last task is to make sure that build dependency is properly set and the "GenerateVersion" project will be built first.

What you achieve after doing all of the above is that all compiled assemblies (.DLL and .EXE) will have a shared version number across multiple assemblies. They will also have in their properties tab information on when they where built and the configuration used during built. That can be used to troubleshoot and to easy production deployments.


Share/Save/Bookmark

Thursday, May 10, 2007

Upgrading application settings

.NET 2.0 has a very nice support for storing custom application settings. It provides a read-only access to the application level settings and read-write access to the user-level settings. For more information check out http://msdn2.microsoft.com/en-us/library/8eyb2ct1(VS.80).aspx

One of the bugs I had to deal with in QueryExPlus is that whenever a new version of the application was released, the settings would get reset to their defaults. The culprit is that settings are stored in the C:\Documents and Settings\\Local Settings\Application Data\\_Url_\\user.config

The location includes the location hash and the version of the executable. The code to keep the old settings is pretty simple.

Add a user level setting called First_Run. Make it boolean and set the default to be False

If this is a first run of the application version, call Upgrade() function, set the First_Run setting so you do not do it again and save the settings.

if (QueryExPlus.Properties.Settings.Default.IsFirstRun)

{

Settings.Default.Upgrade();

Settings.Default.IsFirstRun = false;

Settings.Default.Save();

MessageBox.Show("Settings Upgraded");

}


Share/Save/Bookmark
Directory of Computers/Tech Blogs