Sider

Seneste indlæg

Seneste kommentarer

Vis ikke

Nøgleord

Vis ikke

    Windows Installer - problemer ved opgradering

    I forbindelse med færdiggørelsen af en applikation jeg selv havde skrevet, skulle jeg lave en installer. Under selve udviklingen af applikationen havde jeg ikke skænket installer'en en tanke (noget som MSI holdet ellers anbefaler (jf. regel 5) - men det vidste jeg ikke da jeg begyndte). Under tidspres og på fuldstændig bar bund kastede jeg mig over opgaven.

    Mit første sted at søge information var Visual Studio 2005 off-line hjælpen og MSDN. Det gav ikke umiddelbart pote - hverken med at træffe et hurtigt valg: ClickOnce eller Windows Installer deployment?!! ...og heller ikke med at komme igang. Jeg måtte vende ansigtet ud mod det store internet for at finde brugbar hjælp og her kom en artikel af Arnaldo Sandoval mig især til hjælp. Med den i hånden fik jeg strikket et setup-program på benene, og det har kørt lige siden.

    Imidlertid vokser træerne ikke ind i himlen - som man siger. Derimod vokser software og dermed opdateringer. Med en sådan opdatering af applikationen i hånden, skulle msi'en genoversættes. Selve oversættelsen gik smertefrit, men da jeg skulle afprøve msi'en på en virtuel testmaskine begyndte problemerne at banke på. Jeg fik den meget lidt vejledende fejlbesked (tro mig, jeg er ikke den første hvilket en google-søgning efterfølgende afslørede): "The system administrator has set policies to prevent the installation". Jeg var administrator, så hvori lå problemet?!! Igen var det vanskeligt at finde en løsning på MSDN, så jeg måtte en tur forbi Google. Efter nogen søgning og læsning fandt jeg frem til, at problemet måtte bestå i, at jeg ikke havde ændret versionsnr. og dermed heller ikke givet setup'en en ny "ProductCode". Med disse to ændret løste problemet sig.


    Posted by todobom on 10. november 2008 09:18
    Permalink | Kommentarer (0) | Post RSSRSS comment feed

    Sådan kan Linked Servers sættes op

    I forbindelse med et projekt skulle jeg få to SQL Server'ere til at kommunikere. Jeg havde en stored procedure hvori et postsæt skulle sammensættes af data fra tabeller på to SQL Server'ere. Det drejede sig om ordredata fra kildeserveren som skulle join'es med kundedata fra "remote"-serveren.

    Begge servere befandt sig i samme lokalnet, så forbindelsen mellem dem burde være ligetil. Desværre kunne jeg ikke umiddelbart finde en kort og præcis beskrivelse på nettet som beskrev hvordan det skulle gøres. Det der gav mig hovedbrud var hvor jeg skulle angive ip adressen eller maskinnavnet, så kildeserveren kunne se destinationsserveren. Jeg havde en forestilling om, at feltet "Linked Server" (jævnfør fig. 2) var en slags alias for selve forbindelsen og at der skulle defineres en ip-adresse eller et servernavn andetsteds i konfigurationsboksen.

    SQLServerCentral fandt jeg imidlertid en artikel som bragte mig på sporet. Her følger mine erfaringer som forhåbentlig bringer dig på sporet hurtigere end mig.

    Serverne 

    1. SQL Server 2005 Express installeret på Windows 2003 Server Standard Edition. 
    2. SQL Server 2000 installeret på Windows 2003 Server Standard Edition.

    Fra server 1 skal jeg i en stored procedure join'e data fra server 2. Hertil skal jeg bruge linked servers.

    Sådan gjorde jeg

    Okay, lad os komme igang med det det handler om. Det er egentlig meget simpelt at sætte op. Jeg gjorde det via SQL Server 2005 Management Studio.

    1. Find den serverregistrering hvorfra forbindelsen skal oprettes.
    2. Åbn "Server Objects" -> "Linked Servers".

      Fig. 1
    3. Højreklik på "Linked Servers" og vælg "New Linked Server...". Vinduet "New Linked Server" åbner.
    4. I vinduet vælges "General" og i "Linked server" skrives navnet eller ip-adressen på den server vi skal have fat i. Bemærk, at hvis man skriver et navn, skal det være et navn på en server i netværket.

      New Linked Server dialogboks 1
      Fig. 2

    5. I "Servertype" vælges "SQL Server".
    6. Vi skal herefter sætte sikkerhedsindstillingerne. Gå til "Security".
    7. Under "Security" kan man vælge at tilføje et eller flere logins hvor lokale brugere mappes med brugere på den server der skal oprettes forbindelse til.


      Fig. 3

      Derudover kan man tilføje en slags fallback login hvis ingen i listen blev match'et.

    Posted by todobom on 11. marts 2008 08:54
    Permalink | Kommentarer (0) | Post RSSRSS comment feed

    Tråde og timere

    Her er nogle små eksempler på tråde og timere

    [Denne artikel blev først publiceret på http://todobom.dk/artikler.shtml, men er nu flyttet ind i min blog per 05/05/2010]

    I forbindelse med et projekt på min nuværende arbejdsplads har jeg besluttet at publisere mine overvejelser.

    Opgaven: Udvikl en distribueret client/server-løsning som kan udsende flere hundredetusinde mails om dagen (nope det er ikke spam, men permission marketing - tro mig: jeg afskyr spam og deres ophavsmænd). Selve udsendelsen i sig selv er ikke det store problem. Pt. har vi en ASP3 løsning som fungerer ok, men et ønske om mere kontrol med selve udsendelsen, har drevet udviklingen frem.

    For mig var det en stor mundfuld. Gennem den sidste måneds tid har jeg skullet sætte mig ind i trådprogrammering, .NET Remoting og vende tilbage til OOP - som jeg forlod helt tilbage i 1999, da jeg for alvor begyndte at programmere i ASP. Ja, så det er faktisk syv år siden, jeg sådan rigtig har anvendt OOP.

    Som sagt har vi en kørende løsning som løfter opgaven med at udsende de mange mails. Løsningen baserer sig på ASP3, IIS og IIS'ens SMTP server. Nogen vil sikkert rynke på næsen over det simple setup, men faktisk kører det lynhurtigt. Ihverttilfælde SMTP delen. Derfor vil vi heller ikke ændre på den del.

    Det nye ligger i selve applikationen som fletter de mange emailadresser med nyhedsbrevet. For det første skal applikationen kunne foretage simultane "fletninger" for forskellige nyhedsbreve. For det andet skal applikationen give respons tilbage til en bruger som vil se status på en forsendelse. Via et styrepanel, eller en konsol om man vil, skal en bruger kunne logge sig på applikationen, herefter serveren, og se hvordan en forsendelse står til. Fra styrepanelet skal brugeren kunne stoppe en forsendelse, sætte en forsendelse på pause og udskifte nyhedsbrevet, for derefter at genstarte forsendelsen hvorfra den er kommet.

    I styrepanelet, herefter klienten, skal der være en progressbar som viser hvor langt forsendelsen er kommet. Denne information skal komme fra serveren, som ved hver flettet email skal trigge en event. Denne event skal marshall'es og sendes over et netværk til klienten.

    Opgaven har jeg brudt op i to: en læredel og en udviklingsdel. For det første har jeg skullet høvle mig igennem en anseelig mængde artikler på nettet og MSDN. Artiklerne har været spækket med eksempler som i divergerende grad har kunne bruges i hele eller dele af sit omfang. For det andet har jeg skullet omsætte disse artikler og eksempler til noget brugtbart for min konkrete opgave.

    Efter at have læst artikler og afluret kode, har jeg omsat dem til små praktiske eksempler som behandler en teknologi eller metode i forhold til en specifik problemstilling i projektet. En af dem er asynkron event-handling. Her følger nogle små eksempler:

    Første eksempel demonstrerer hvordan en klasse (Asynkron) som implementerer en asynkron metode (StartAsync) kan kaldes fra en anden klasse (Program). Klassen Program knytter event'en Asynkron.Færdig til en handler.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                List liste = new List();

                Asynkron asynkron1 = new Asynkron(3000, "den første");
                asynkron1.Færdig += new EventHandler(asynkron_Færdig);
                liste.Add(asynkron1);
                asynkron1.StartAsync();

                Asynkron asynkron2 = new Asynkron(8000, "den anden");
                asynkron2.Færdig += new EventHandler(asynkron_Færdig);
                liste.Add(asynkron2);
                asynkron2.StartAsync();

                Console.WriteLine("Tryk på tastaturet for at afslutte");
                Console.ReadKey();
            }

            static void asynkron_Færdig(object sender, EventArgs e)
            {
                Console.WriteLine(((Asynkron)sender).navn + " er endelig færdig");
            }
        }

        class Asynkron
        {
            Thread worker;
            public event EventHandler Færdig;
            public String navn;
            private int hvorLænge;

            public Asynkron(int hvorLænge, String navn)
            {
                this.navn = navn;
                this.hvorLænge = hvorLænge;
            }

            protected void OnFærdig(EventArgs e)
            {
                EventHandler handler = Færdig;
                if (handler != null)
                    handler(this, e);
            }

            public void StartAsync()
            {
                worker = new Thread(Hænger);
                worker.IsBackground = true;
                worker.Start();
            }

            private void Hænger()
            {
                Thread.Sleep(hvorLænge);
                OnFærdig(new EventArgs());
            }
        }
    }

    Eksempel 2

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;

    namespace ConsoleApplication1
    {
        class Program
        {
            static List liste = new List();

            class Tæller
            {
                public int i = 2;
            }

            static Tæller tæller = new Tæller();
            static TimerCallback timerDelegate = new TimerCallback(TimerTick);
            static Timer timer = new Timer(timerDelegate, null, 2000, 2000);

            static void Main(string[] args)
            {
                Console.WriteLine("Tryk på tastaturet for at afslutte");
                Console.ReadKey();
            }

            private static void TimerTick(Object data)
            {
                lock (tæller)
                {
                    if (tæller.i == 0)
                    {
                        Console.Write(".");
                        return;
                    }

                    Asynkron asynkron = new Asynkron(3 * tæller.i, tæller.i.ToString());
                    tæller.i--;
                    asynkron.Færdig += new EventHandler(asynkron_Færdig);
                    liste.Add(asynkron);
                    asynkron.StartAsync();
                }
            }

            static void asynkron_Færdig(object sender, EventArgs e)
            {
                Console.WriteLine(((Asynkron)sender).navn + " er endelig færdig");
            }
        }

        class Asynkron
        {
            Thread worker;

            public event EventHandler Færdig;
            public String navn;
            private int hvorLænge;

            public Asynkron(int hvorLænge, String navn)
            {
                //Console.WriteLine(navn + "");
                this.navn = navn;
                this.hvorLænge = hvorLænge;
            }

            protected void OnFærdig(EventArgs e)
            {
                EventHandler handler = Færdig;

                if (handler != null)
                    handler(this, e);
            }

            public void StartAsync()
            {
                worker = new Thread(Hænger);
                worker.IsBackground = true;
                worker.Start();
            }

            private void Hænger()
            {
                Thread.Sleep(hvorLænge);
                OnFærdig(new EventArgs());
            }
        }
    }


    Posted by todobom on 24. november 2006 10:08
    Permalink | Kommentarer (0) | Post RSSRSS comment feed