ADO.NET vs. LinQ to SQL

Fordele

Der er flere fordele ved LinQ to SQL over ADO.NET. Nogle eksempler er følgende:

  • Ingen brug for at skrive triviel kode til databasekald.
  • Stærke typer gør at flere fejl fanges ved compile-time.
  • Visual Studio intellisense resulterer i kortere udviklingstid.
  • Lambda expressions resulterer i kortere udviklingstid og mere kompakt og forståelig kode.

Hastighed

I de fleste situationer er der ikke grund til at spekulere på om LinQ to SQL har værre performance end ADO.NET. De største performanceforbedringer findes typisk tre andre steder:

  1. Udførelse af operationer på data på SQL serveren når det er muligt.
  2. Benyttelse af fornuftige indeks og evt. midlertidige tabeller
  3. Reducering af mængden af data som skal overføres til applikationen.

Opsætning af LinQ to SQL database context

Opret et projekt af typen Console.

Højreklik på projektet, vælg Add New Item -> Data -> LINQ to SQL Classes. Sæt Name til “MyDatabase.dbml” og klik Add.

Klik på Server Explorer.

Højreklik på Data Connections og vælg Add Connection…

Skriv “.\SQLEXPRESS” i Server name og klik på Refresh. Der bør ikke meldes om fejl.

Skriv “MyDatabase” i New database name og klik OK.

Opret en tabel Customers med følgende struktur:

create table Customers (ID int NOT NULL IDENTITY, FirstName nvarchar(100), LastName nvarchar(100), BirthDate datetime, Active bit, constraint Customers_ID primary key (ID))

Træk tabellen Customers over i MyDatabase.dbml vinduet og gem filen.

Indsættelse af entiteter med ADO.NET

Vi fodrer et SqlCommand objekt med et SQL statement og udfører kommandoen på et åbent SqlConnection objekt.

private static void AddCustomer(SqlConnection connection, string firstName, string lastName, DateTime birthDate, bool active)
{
    var command = new SqlCommand("insert into Customers (FirstName, LastName, BirthDate, Active) VALUES (@FirstName, @LastName, @BirthDate, @Active)", connection);
    command.Parameters.Add(new SqlParameter("@FirstName", firstName));
    command.Parameters.Add(new SqlParameter("@LastName", lastName));
    command.Parameters.Add(new SqlParameter("@BirthDate", birthDate));
    command.Parameters.Add(new SqlParameter("@Active", active));
    command.ExecuteNonQuery();
}

Som kan ses, så kræver det en del triviel kode.

Udtræk af entiteter med ADO.NET

Vi fodrer en connection string til et SqlConnection objekt, åbner forbindelsen og indsætter nogle entiteter. For at hente entiteterne frem igen, fodrer vi SQL til SqlCommand, opretter en SqlDataReader, gennemgår hver række samt henter værdien af hvert felt vha. feltnavnindeks. Læg mærke til det trivielle kode vi benytter til bare at konvertere til den rette datatype. Koder vi fejl her, så opdager vi det sandsynligvis først ved run-time ved test (hvis vi da sørger for at teste kodevejen med fejlen) eller (endnu værre) ved run-time i produktionmiljøet. Typefejl opstår ikke så ofte med LinQ to SQL, men kan ske hvis .dbml filen ikke er opdateret til databasestrukturen.

static void Ado()
{
    using (var connection = new SqlConnection(ConnectionString))
    {
        connection.Open();
        AddCustomer(connection, "Hans", "Christian", new DateTime(1960, 10, 4), true);
        AddCustomer(connection, "George", "Kohl", new DateTime(1974, 5, 7), true);
        AddCustomer(connection, "Marie", "Muse", new DateTime(1986, 2, 8), false);
        var command = new SqlCommand("select FirstName, LastName, BirthDate, Active from Customers", connection);
        var customersReader = command.ExecuteReader(CommandBehavior.CloseConnection);
        while (customersReader != null && customersReader.Read())
            Console.WriteLine(String.Format("{0} {1} {2} ({3})",
                (string)customersReader["FirstName"],
                (string)customersReader["LastName"],
                ((DateTime)customersReader["BirthDate"]).ToString("dd-MM-yyyy"),
                (bool)customersReader["Active"]));
    }
}

Indsættelse og udtrækning af objekter med LinQ to SQL

Med LinQ to SQL opretter vi et MyDatabaseDataContext objekt som svarer til SqlConnection objektet i ADO.NET. Vi fodrer objektet med en connection string.

Som det kan ses så er koden til indsættelse så triviel at vi ikke behøver at pakke det ind i egen metode for at få overskuelig kode. Det kræver kun to linjer kode. Vi opretter et Customer-objekt, fodrer objektet til <datakontekst>.<objekt-type>s.InsertOnSubmit() samt kalder <datakontekst>.SubmitChanges() for at igangsætte ændringerne i databasen.

Med LinQ to SQL kan vi udtrække data på to måder.

1) SQL lignende syntax. Struktur:

Denne form kan være god at bruge i de lidt mere komplekse tilfælde med joins. LinQ to SQL sørger for at hentes mindst muligt data fra databasen.

var variable1 = from <id1> in <datakontekst>.<tabelnavn1>s
                [join <id2> in <datakontekst>.<tabelnavn1>s on <id1>.<key1> equals <id2>.<key2>]
                [where]
                [<id1>.<feltnavn1>.<metode>(<parametre>)]
                [&& <id1>.<feltnavn2> <operator> <værdi>]
                [orderby <id1>.<feltnavn1> [ascending|descending]]
                select <id1>;

Hvis meget kompleks datamanipulation eller udtræk er nødvendigt, så kan man med fordel oprette en stored procedure og kalde denne ved brug af LinQ to SQL. Metoder til at kalde stored procedures placeres på datakontekstobjektet.

Det er også muligt at instansiere et objekt af en type som opbygges on-the-fly ved at ændre “select <id1>” til:

select new
{
    <feltnavn1> = <udtryk>,
    <feltnavn2> = <udtryk>
};

2) Metodekald med lambda expressions. Struktur:

var customers2 = <datakontekst>.<tabelnavn>s.Where(<id> => [<id>.<feltnavn>.<metode>(<parametre>)] <operator> 
[<id>.<feltnavn> <operator> <value>]);

Vi kan nu bare iterere igennem resultatsættet som består af stærkt typede objekter f.eks. med foreach. Eksempel:

static void Linq()
{
    using (var db = new MyDatabaseDataContext(ConnectionString))
    {
        db.Customers.InsertOnSubmit(new Customer { FirstName = "LinQ", LastName = "Rocks", BirthDate = new DateTime(2006, 4, 11), Active = false});
        db.SubmitChanges();

        var customers1 = from c in db.Customers
                        where c.FirstName.StartsWith("H") &&
                              c.Active == true
        orderby c.BirthDate ascending
                        select c;

        foreach (var customer in customers1)
            Console.WriteLine(String.Format("1: {0} {1} {2} ({3})",
                customer.FirstName,
                customer.LastName,
                customer.BirthDate.ToString(),
                customer.Active));

        var customers2 = db.Customers.Where(c => c.FirstName.StartsWith("H") && c.Active == true);
        foreach (var customer in customers2)
            Console.WriteLine(String.Format("2: {0} {1} {2} ({3})",
                customer.FirstName,
                customer.LastName,
                customer.BirthDate.ToString(),
                customer.Active));

        var customers3 = from c in db.Customers
                        where c.Active == false
                        select new
                        {
                            FullName = c.FirstName + " " + c.LastName,
                            BirthDate = c.BirthDate,
                        };

        foreach (var customer in customers3)
            Console.WriteLine(String.Format("3: {0} {1}",
                customer.FullName,
                customer.BirthDate.ToString()));
    }
}

Resultatet fra Linq() metoden er:

1: Hans Christian 04-10-1960 00:00:00 (True)
2: Hans Christian 04-10-1960 00:00:00 (True)
3: Marie Muse 08-02-1986 00:00:00
3: LinQ Rocks 11-04-2006 00:00:00