228/2008

Det var nära två år sen Microsoft visade upp demos på sin fototeknik Photosynth, som skapar 3D-miljöer från foton. Sen dess har den blivit väldigt hypad och bland annat används i ett avsnitt av den amerikanska tv-serien CSI.
Idag så släppte de äntligen tjänsten för allmänheten och man kan nu skapa ett konto där det går att ladda upp 20 MB bilder. Under dagen har tjänsten varit väldigt överbelastad och det gick inte ens att nå sidan för mig förrän nu på natten. I bloggen skriver de om hur hela sajten krashade av all trafik.
För tillfället går tjänsten bara att använda med Internet Explorer och Microsoft meddelar att det inte går att köra på Mac. Teamet bakom viritualiseringsmotorn VMware Fusion bevisade dock under dagen att de har fel och det kan jag också bekräffa.
Man behöver ha ett MS-passportkonto när man anmäler sig och processen gick snabbt och lätt. Testkörde en synth som ni kan se en screenshot på här ovan. Det är nästa omöjligt att visa hur det fungerar på en vanlig bild hur häftigt det är, så jag rekommenderar att ni besöker sajten själva för att verkligen se vad det handlar om. Bilderna man laddar upp fogas alltså tillsammans där de överlappar och programmet bygger upp en 3D-modell på det man fotat. Man kan sedan flyga runt i modellen med hjälp av tangenterna som i ett "first-person-shooter"-spel eller arrangera dem sida vid sida och annat skoj. Ett slagt superpanorama kan man säga.
Det gäller att alla bilderna i sin samling hänger ihop med tillräckligt många detaljer för att tjänsten ska kunna skapa en fullständig sammanfogning. I mitt fall så blev de tre fristående "öar" av bilder och den kunde bara sammanfoga samlingen till 56%. Jag använde bilder på ett träd och den fattade inte riktigt att jag hade bilder från olika håll av samma objekt. Men ändå är resultatet förbluffande häftigt även när jag bara slängde upp den första grupp bilder jag hade i fotoalbumet. Tänk då vad man kan skapa om man lägger ner lite jobb på att förbereda bilderna och ta foton särskilt för detta ändamål.
Många har trott att själva processen att skapa 3D-miljöerna skulle ta väldigt lång tid, men jag tyckte det gick väldigt snabbt med de bilder jag testkörde med. Det som tog tid var själva uppladdningen till sajten och det kan man ju förstå, speciellt eftersom jag hade med några tunga rackare i mappen.
Photosynth är verkligen en höjdartjänst som jag vet att jag kommer ha mycket kul med. Riktigt roligt att den äntligen är igång.
Av Jesper Lind
77/2008
Ett litet tips från ett gammalt inlägg på Coding Horror, som visar hur man använder CTRL+I för att göra ökningsbar sökning i Visual Studio.
Hade jag själv ingen aning om att man kunde göra och känns väldigt användbart för att texter snabbt. Bra att ha när ens medarbetare gömmer koden i #regions till exempel.
Av Jesper Lind
77/2008

APPLY (msdn) är ett nytt SQL-kommando i SQL Server från och med version 2005 och finns i två olika varianter, CROSS APPLY och OUTER APPLY.
Kan inte säga att jag förstått dessa funktionerna helt, men jag har hittat ett bra användningsområde för CROSS APPLY. Nämligen att kunna bygga upp VIEWs med data från en relationstabell och sammanfoga dessa i en ny kolumn separerade med valfritt tecken. Detta kallas tydligen "aggregate concatenation".
Läste även att denna metod går emot huvudideén för hur en relationsdatabas är tänkt att fungera, så har du ett behov att göra såna här SQL-frågor kan det vara snart att tänka om ifall det går att göra på ett bättre sätt. Eller kanske hämta ut datan separat och bygga ihop vyn i buisness-lagret.
Kommer här visa hur det kan fungera genom ett exempel, här först databasdiagrammet.
Sen ett skript (skapat med SubSonic) som bygger upp hela databasen:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Products]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](150) COLLATE Finnish_Swedish_CI_AS NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: Table [dbo].[Colors] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Colors]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Colors](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ColorName] [nvarchar](150) COLLATE Finnish_Swedish_CI_AS NULL,
CONSTRAINT [PK_Colors] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: Table [dbo].[ProductColor] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ProductColor]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[ProductColor](
[ProductId] [int] NOT NULL,
[ColorId] [int] NOT NULL,
CONSTRAINT [PK_ProductColor] PRIMARY KEY CLUSTERED
(
[ProductId] ASC,
[ColorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: View [dbo].[ListOfColorsView] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[ListOfColorsView]'))
EXEC dbo.sp_executesql @statement = N'CREATE VIEW dbo.ListOfColorsView
AS
SELECT DISTINCT ProductName, ListOfColors = LEFT(o.list, LEN(o.list) - 1)
FROM Products CROSS APPLY
(SELECT CONVERT(VARCHAR(12), ColorName) + '','' AS [text()]
FROM dbo.Colors c INNER JOIN
dbo.ProductColor ON c.Id = dbo.ProductColor.ColorId
WHERE (dbo.ProductColor.ProductId = dbo.Products.Id)
ORDER BY c.ColorName FOR XML PATH('''')) o(list)
'
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ProductColor_Colors]') AND parent_object_id = OBJECT_ID(N'[dbo].[ProductColor]'))
ALTER TABLE [dbo].[ProductColor] WITH CHECK ADD CONSTRAINT [FK_ProductColor_Colors] FOREIGN KEY([ColorId])
REFERENCES [Colors] ([Id])
ALTER TABLE [dbo].[ProductColor] CHECK CONSTRAINT [FK_ProductColor_Colors]
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ProductColor_Products]') AND parent_object_id = OBJECT_ID(N'[dbo].[ProductColor]'))
ALTER TABLE [dbo].[ProductColor] WITH CHECK ADD CONSTRAINT [FK_ProductColor_Products] FOREIGN KEY([ProductId])
REFERENCES [Products] ([Id])
ALTER TABLE [dbo].[ProductColor] CHECK CONSTRAINT [FK_ProductColor_Products]
Och lite testdata:
ALTER TABLE [Colors] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [Colors] DISABLE TRIGGER ALL
GO
SET IDENTITY_INSERT [Colors] ON
PRINT 'Begin inserting data in Colors'
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(1, 'Red')
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(2, 'Blue')
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(3, 'Green')
SET IDENTITY_INSERT [Colors] OFF
ALTER TABLE [Colors] CHECK CONSTRAINT ALL
GO
ALTER TABLE [Colors] ENABLE TRIGGER ALL
GO
ALTER TABLE [Products] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [Products] DISABLE TRIGGER ALL
GO
SET IDENTITY_INSERT [Products] ON
PRINT 'Begin inserting data in Products'
INSERT INTO [Products] ([Id], [ProductName])
VALUES(1, 'Test product 1')
INSERT INTO [Products] ([Id], [ProductName])
VALUES(2, 'Test product 2 - No Colors')
SET IDENTITY_INSERT [Products] OFF
ALTER TABLE [Products] CHECK CONSTRAINT ALL
GO
ALTER TABLE [Products] ENABLE TRIGGER ALL
GO
ALTER TABLE [ProductColor] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [ProductColor] DISABLE TRIGGER ALL
GO
PRINT 'Begin inserting data in ProductColor'
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 1)
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 2)
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 3)
ALTER TABLE [ProductColor] CHECK CONSTRAINT ALL
GO
ALTER TABLE [ProductColor] ENABLE TRIGGER ALL
GO
Det mest intressanta är ju själva vyn som separerar datan från relationstabellen med ett kommatecken mellan varje värde.
SELECT DISTINCT ProductName, ListOfColors = LEFT(o.list, LEN(o.list) - 1)
FROM Products CROSS APPLY
(SELECT CONVERT(VARCHAR(12), ColorName) + ',' AS [text()]
FROM dbo.Colors c INNER JOIN
dbo.ProductColor ON c.Id = dbo.ProductColor.ColorId
WHERE (dbo.ProductColor.ProductId = dbo.Products.Id)
ORDER BY c.ColorName FOR XML PATH('')) o(list)
Svaret från denna vyn ser ut så här:
Test product 1 Blue,Green,Red
Test product 2 - No Colors NULL
Mer läsning:
http://zulfiqar.typepad.com/zulfiqars_web/2005/04/tsql_concatenat.html
http://blog.sqlauthority.com/2008/01/04/sql-server-2005-cross-apply/
Av Jesper Lind
37/2008
Den här har nog redan gått några virala varv runt "the intertubes" men har du inte sett den rekommenderas den starkt. Varning för stundvis omogen humor och dåligt språk, men den är i många delar helt klockren. Vi har haft rätt mycket kul åt den här på kontoret och har svårt att oss på själva på allvar längre. Om vi nu någonsin har gjort det ;)
Orginalet finns på thewebsiteisdown.com och har på den senaste veckan blivit så populär så att själva sajten har krashat några gånger (ödets ironi...)
Av Jesper Lind
37/2008

Vi har börjat testa lite smått på några webbsajter att använda ISAPI-modulen som finns installerad hos Binero och det fungerar ganska bra. Vi har till och med fått igång ett första MVC-projekt på .NET 3.5. Riktigt kul med ett webbhotell som är framåt och förstår vilken teknik vi utvecklare vill ha.
Det tog lite tid att lista ut hur man fick igång det eftersom Bineros förklaring av Rewrite 3 inte är så utförlig. De har manualen på deras sajt så där kan man lära sig lite (funkar dock inte med Firefox 3). Helicon Tech som ligger bakom modulen rekommenderas också.
Mest hjälp för att få igång det hade jag av av några trådar på Aspsidan och webForum.
Något att tänka på är att de ändringar man gör i Script Mappings i Bineros kontrollpanel tar ett tag innan de slår igenom. Kommer ihåg den första kvällen då jag kämpade med det in på småtimmarna men hade ingen lycka. Provade mängder av olika konfigurationer och gav sist upp och bara lämnade det. Nästa morgon när jag laddade upp sidan så bara det fungerade!
Så nu ska jag förklara det jag kommit på hittills.
Det första man måste göra är att lägga till en *-mappning enligt följande.
Filetype: *
Executable: Aspnet2
is wildcard: false
is script engine: false
verify that file exists: false
När du ändå är där kan du även lägga till .asax eftersom Binero verkar ha glömt det i standardinställningen. Se bilden för exempel på de två inställningarna längst ner.
Sen är det dags att knåpa ihop en httpd.ini (uppdatering: filen ska ha namnet .htaccess och inget annat) som styr hur ISAPI Rewrite 3 ska uppföra sig. Här under ser ni våran som fungerar fint om man vill ta bort .aspx ändelserna. Jag har dock inte lyckats att exkludera vissa mappar, så om någon kan se vad det är för fel på sista raden som ska blocka mappen "MappSomInteSkaOmskrivas" så uppskattar jag tips. Uppdatering: Denna rad måste ju givetvis ligga överst för att de andra reglerna ska ignoreras. Har nu uppdaterat skriptet som det ska se ut och då funkar det mycket bättre.
# Helicon ISAPI_Rewrite configuration file
# Version 3.1.0.34
RewriteEngine on
#Exclude folder
RewriteRule /(?!(?:Images|Css|MappSomInteSkaOmskrivas)/.*).* - [L]
#Redirect extension requests to avoid duplicate content
RewriteRule ^([^?]+)\.aspx$ $1 [NC,R=301]
#Internally add extensions to request
RewriteCond %{REQUEST_FILENAME}.aspx -f
RewriteRule (.*) $1.aspx
Av Jesper Lind
256/2008
Har funderat på ibland hur man kan göra foor-loop i SQL Server. Efter en sökning hittade jag svaret hos SQLAuthority som också visar upp exempel på break och continue.
I mitt fall ville jag köra en insert för varje loopning och mitt skript såg ut ungefär som följer. Variablen intFlag används också för att sätta in ett stigande heltal i tabellen.
DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=100)
BEGIN
PRINT @intFlag
INSERT INTO TheTable (IntValue,StringValue,CounterValue) VALUES (1,'MyDefaultString,@intFlag))
SET @intFlag = @intFlag + 1
END
GO
Av Jesper Lind
206/2008

Via inUseful hittade jag den roliga tjänsten Wordle som med hjälp av en Java-applet skapar taggmoln av text som användarna matar in. Går även att hämta taggar från del.icio.us vilket jag gjorde till bilden ovan. Sparade den även i galleriet på Wordle ifall ni vill se mitt taggmoln i större format.
Av Jesper Lind
46/2008
SubSonic är riktigt trevligt att jobba med och vi använder det mer och mer i våra projekt. Dels så är det smidigt att få ett helt databaslager skapat automatiskt, men även bra till att skripta ut struktur och data från databaser.
Ibland vill man bara ha några enstaka tabeller och då kan man skriva enlgt följande i sin App.config. Hittade hur man gör för att skriva in Regex-uttryck i fältet includeTableList i SubSonic-forumet.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="SubSonicService" type="SubSonic.SubSonicSection, SubSonic" requirePermission="false"/>
</configSections>
<connectionStrings>
<add name="ConnectionString" connectionString="Data Source=(local);Initial Catalog=MyLocalDatabase;Integrated Security=True"/>
</connectionStrings>
<SubSonicService defaultProvider="Provider" >
<providers>
<clear/>
<add name="Provider"
type="SubSonic.SqlDataProvider, SubSonic" includeTableList="\b(Table1|Table2|ThirdTable)\b" connectionStringName="ConnectionString" generatedNamespace="MyNamespace.DAL" />
</providers>
</SubSonicService>
</configuration>
Av Jesper Lind
315/2008
Vi har tidigare visat hur man kan göra för att kopiera ett enskild fält i SQL Server mellan två databaser. Idag är det dags för att visa hur man kopierar all data i en tabell. Det man ska tänka på är att tillfälligt tillåta insättning av primärnycklar, för att dessa ska gå att kopiera.
SET IDENTITY_INSERT [TargetDB].[dbo].[TableToCopy] ON
INSERT INTO [TargetDB].[dbo].[TableToCopy] (Id,Field1,AnotherField,ABoolField)
SELECT Id,Field1,AnotherField,ABoolField FROM [SourceDB].[dbo].[TableToCopy]
SET IDENTITY_INSERT [TargetDB].[dbo].[TableToCopy] OFF
Av Jesper Lind