Dapper.NET
Multimapping
Sök…
Syntax
-
public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>( this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
-
public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
-
public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
parametrar
Parameter | detaljer |
---|---|
cnn | Din databasanslutning, som redan måste vara öppen. |
sQL | Kommando att utföra. |
typer | Array av typer i postuppsättningen. |
Karta | Func<> som hanterar konstruktionen av returresultatet. |
param | Objekt att extrahera parametrar från. |
transaktion | Transaktion som denna fråga är en del av, om någon. |
buffrad | Huruvida buffertläsning av resultaten från frågan ska göras eller inte. Detta är en valfri parameter med standardinställningen sant. När buffrat är sant buffras resultaten till en List<T> och returneras sedan som ett IEnumerable<T> som är säkert för flera uppräkning. När buffrat är felaktigt hålls sql-anslutningen öppen tills du är klar med läsningen så att du kan behandla en enda rad samtidigt i minnet. Flera uppräkningar kommer att leda till ytterligare anslutningar till databasen. Även om buffrad falsk är mycket effektiv för att minska minnesanvändningen om du bara behåller mycket små fragment av de poster som återlämnats har den en betydande prestanda jämfört med ivrigt materialisering av resultatuppsättningen. Slutligen, om du har många samtidiga obuffrade sql-anslutningar, måste du överväga anslutning till poolen svält vilket orsakar förfrågningar om att blockera tills anslutningarna blir tillgängliga. |
dela på | Fältet vi ska dela och läsa det andra objektet från (standard: id). Detta kan vara en kommaavgränsad lista när mer än 1 typ finns i en post. |
commandTimeout | Antal sekunder innan timeout för kommandokörning. |
Command | Är det en lagrad proc eller en batch? |
Enkel kartläggning av flera bord
Låt oss säga att vi har en fråga om de återstående ryttarna som behöver fylla en personklass.
namn | Född | Bostad |
---|---|---|
Daniel Dennett | 1942 | Amerikas förenta stater |
Sam Harris | 1967 | Amerikas förenta stater |
Richard Dawkins | 1941 | Storbritannien |
public class Person
{
public string Name { get; set; }
public int Born { get; set; }
public Country Residience { get; set; }
}
public class Country
{
public string Residence { get; set; }
}
Vi kan fylla såväl personklassen som bostadsfastigheten med en instans av land med hjälp av en överbelastningsfråga Query<>
som tar en Func<>
som kan användas för att komponera den returnerade instansen. Func<>
kan ta upp till sju ingångstyper med det slutliga generiska argumentet som alltid är returtypen.
var sql = @"SELECT 'Daniel Dennett' AS Name, 1942 AS Born, 'United States of America' AS Residence
UNION ALL SELECT 'Sam Harris' AS Name, 1967 AS Born, 'United States of America' AS Residence
UNION ALL SELECT 'Richard Dawkins' AS Name, 1941 AS Born, 'United Kingdom' AS Residence";
var result = connection.Query<Person, Country, Person>(sql, (person, country) => {
if(country == null)
{
country = new Country { Residence = "" };
}
person.Residience = country;
return person;
},
splitOn: "Residence");
Notera användningen av
splitOn: "Residence"
-argumentet som är den första kolumnen i nästa klasstyp som ska fyllas (i detta fallCountry
). Dapper letar automatiskt efter en kolumn som heter Id att dela på men om den inte hittar en ochsplitOn
inte tillhandahålls ettSystem.ArgumentException
kastas med ett användbart meddelande. Så även om det är valfritt måste du vanligtvissplitOn
ettsplitOn
värde.
En-till-många-kartläggning
Låt oss titta på ett mer komplext exempel som innehåller en en-till-många-relation. Vår fråga kommer nu att innehålla flera rader som innehåller duplicerade data och vi måste hantera detta. Vi gör detta med en uppslagning i en stängning.
Frågan ändras något liksom exempelklasserna.
Id | namn | Född | CountryId | Lands namn | BookId | BookName |
---|---|---|---|---|---|---|
1 | Daniel Dennett | 1942 | 1 | Amerikas förenta stater | 1 | brainstorming |
1 | Daniel Dennett | 1942 | 1 | Amerikas förenta stater | 2 | Svängrum |
2 | Sam Harris | 1967 | 1 | Amerikas förenta stater | 3 | Det moraliska landskapet |
2 | Sam Harris | 1967 | 1 | Amerikas förenta stater | 4 | Vakna upp: En guide till andlighet utan religion |
3 | Richard Dawkins | 1941 | 2 | Storbritannien | 5 | Verklighetens magi: hur vi vet vad som verkligen är sant |
3 | Richard Dawkins | 1941 | 2 | Storbritannien | 6 | En aptit för undring: skapandet av en forskare |
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Born { get; set; }
public Country Residience { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string CountryName { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string BookName { get; set; }
}
Den remainingHorsemen
ordboken remainingHorsemen
kommer att fyllas med fullständigt materialiserade instanser av personobjekten. För varje rad i frågeställningen ges de mappade värdena på instanser av de typer som definieras i lambda-argumenten och det är upp till dig hur du hanterar detta.
var sql = @"SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 1 AS BookId, 'Brainstorms' AS BookName
UNION ALL SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 2 AS BookId, 'Elbow Room' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 3 AS BookId, 'The Moral Landscape' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 4 AS BookId, 'Waking Up: A Guide to Spirituality Without Religion' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId, 'United Kingdom' AS CountryName, 5 AS BookId, 'The Magic of Reality: How We Know What`s Really True' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId, 'United Kingdom' AS CountryName, 6 AS BookId, 'An Appetite for Wonder: The Making of a Scientist' AS BookName";
var remainingHorsemen = new Dictionary<int, Person>();
connection.Query<Person, Country, Book, Person>(sql, (person, country, book) => {
//person
Person personEntity;
//trip
if (!remainingHorsemen.TryGetValue(person.Id, out personEntity))
{
remainingHorsemen.Add(person.Id, personEntity = person);
}
//country
if(personEntity.Residience == null)
{
if (country == null)
{
country = new Country { CountryName = "" };
}
personEntity.Residience = country;
}
//books
if(personEntity.Books == null)
{
personEntity.Books = new List<Book>();
}
if (book != null)
{
if (!personEntity.Books.Any(x => x.BookId == book.BookId))
{
personEntity.Books.Add(book);
}
}
return personEntity;
},
splitOn: "CountryId,BookId");
Lägg märke till hur
splitOn
argumentet är en kommaavgränsad lista över de första kolumnerna av nästa typ.
Kartlägga mer än sju typer
Ibland överstiger antalet typer du kartlägger de 7 som tillhandahålls av Func <> som utför konstruktionen.
Istället för att använda Query<>
med de generiska argumentinmatningarna kommer vi att tillhandahålla de typer som ska kartläggas som en matris, följt av mappningsfunktionen. Förutom den initiala manuella inställningen och gjutningen av värdena, ändras inte resten av funktionen.
var sql = @"SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 1 AS BookId, 'Brainstorms' AS BookName
UNION ALL SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 2 AS BookId, 'Elbow Room' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 3 AS BookId, 'The Moral Landscape' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 4 AS BookId, 'Waking Up: A Guide to Spirituality Without Religion' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId, 'United Kingdom' AS CountryName, 5 AS BookId, 'The Magic of Reality: How We Know What`s Really True' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId, 'United Kingdom' AS CountryName, 6 AS BookId, 'An Appetite for Wonder: The Making of a Scientist' AS BookName";
var remainingHorsemen = new Dictionary<int, Person>();
connection.Query<Person>(sql,
new[]
{
typeof(Person),
typeof(Country),
typeof(Book)
}
, obj => {
Person person = obj[0] as Person;
Country country = obj[1] as Country;
Book book = obj[2] as Book;
//person
Person personEntity;
//trip
if (!remainingHorsemen.TryGetValue(person.Id, out personEntity))
{
remainingHorsemen.Add(person.Id, personEntity = person);
}
//country
if(personEntity.Residience == null)
{
if (country == null)
{
country = new Country { CountryName = "" };
}
personEntity.Residience = country;
}
//books
if(personEntity.Books == null)
{
personEntity.Books = new List<Book>();
}
if (book != null)
{
if (!personEntity.Books.Any(x => x.BookId == book.BookId))
{
personEntity.Books.Add(book);
}
}
return personEntity;
},
splitOn: "CountryId,BookId");
Anpassade kartläggningar
Om namnet på frågekolumnen inte stämmer med dina klasser kan du ställa in mappningar för typer. Detta exempel visar kartläggning med System.Data.Linq.Mapping.ColumnAttribute
såväl som en anpassad kartläggning.
Kartläggningarna behöver bara konfigureras en gång per typ så ställ dem in vid programstart eller någon annanstans att de bara initialiseras en gång.
Antagande samma fråga som exemplet En-till-många igen och klasserna refaktorerade mot bättre namn som så:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Born { get; set; }
public Country Residience { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Country
{
[System.Data.Linq.Mapping.Column(Name = "CountryId")]
public int Id { get; set; }
[System.Data.Linq.Mapping.Column(Name = "CountryName")]
public string Name { get; set; }
}
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
}
Lägg märke till hur
Book
inte förlitar sig påColumnAttribute
men vi måste behållaif
uttalandet
Placera nu denna kartläggningskod någonstans i din applikation där den bara körs en gång:
Dapper.SqlMapper.SetTypeMap(
typeof(Country),
new CustomPropertyTypeMap(
typeof(Country),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<System.Data.Linq.Mapping.ColumnAttribute>()
.Any(attr => attr.Name == columnName)))
);
var bookMap = new CustomPropertyTypeMap(
typeof(Book),
(type, columnName) =>
{
if(columnName == "BookId")
{
return type.GetProperty("Id");
}
if (columnName == "BookName")
{
return type.GetProperty("Name");
}
throw new InvalidOperationException($"No matching mapping for {columnName}");
}
);
Dapper.SqlMapper.SetTypeMap(typeof(Book), bookMap);
Sedan körs frågan med hjälp av något av de tidigare Query<>
exemplen.
Ett enklare sätt att lägga till mappningarna visas i det här svaret .