수색…
통사론
-
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)
매개 변수
매개 변수 | 세부 |
---|---|
CNN | 데이터베이스 연결이 이미 열려 있어야합니다. |
SQL | 실행할 명령. |
유형 | 레코드 세트에있는 유형의 배열. |
지도 | 반환 결과의 생성을 처리하는 Func<> |
매개 변수 | 매개 변수를 추출 할 객체입니다. |
트랜잭션 | 이 쿼리가있을 경우 트랜잭션의 일부입니다. |
완충 된 | 쿼리의 결과를 버퍼링할지 여부. 이 매개 변수는 선택적 매개 변수이며 기본값은 true입니다. buffered가 true 일 때 결과는 List<T> 버퍼링 된 다음 여러 열거 형에 안전한 IEnumerable<T> 로 반환됩니다. buffered가 false이면 sql 연결은 읽기가 끝날 때까지 열려있어 메모리에서 한 번에 한 행을 처리 할 수 있습니다. 여러 열거 형은 데이터베이스에 대한 추가 연결을 생성합니다. 버퍼가있는 false는 반환되는 레코드의 아주 작은 조각 만 유지하는 경우 메모리 사용을 줄이는 데 매우 효율적이지만 결과 집합을 열심히 구현하는 것과 비교할 때 상당한 성능 오버 헤드 가 있습니다. 마지막으로 수많은 버퍼링되지 않은 sql 연결이있는 경우 연결을 사용할 수있게 될 때까지 연결 풀 기아가 요청을 차단하도록 고려해야합니다. |
splitOn | 필드는 두 번째 객체를 분할하여 읽어야합니다 (기본값 : id). 레코드에 둘 이상의 유형이 포함되어 있으면 쉼표로 구분 된 목록이 될 수 있습니다. |
commandTimeout | 명령 실행 제한 시간 (초)입니다. |
commandType | 저장 프로 시저 또는 배치입니까? |
간단한 다중 테이블 매핑
Person 클래스를 채워야하는 나머지 기수에 대한 쿼리가 있다고 가정 해 봅시다.
이름 | 타고난 | 거주 |
---|---|---|
다니엘 데닛 | 1942 | 아메리카 합중국 |
샘 해리스 | 1967 년 | 아메리카 합중국 |
리차드 도킨스 | 1941 | 영국 |
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; }
}
우리는 반환 된 인스턴스를 작성하는 데 사용할 수있는 Func<>
을 사용하는 오버로드 Query<>
를 사용하여 Person 클래스뿐만 아니라 Residence 속성을 Country 인스턴스로 채울 수 있습니다. Func<>
은 입력 형식을 최대 7 개까지 취할 수 있으며 최종 일반 인수는 항상 반환 형식입니다.
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");
splitOn: "Residence"
채우기 될 다음 클래스 유형의 첫 번째 열인splitOn: "Residence"
인수를 사용합니다 (이 경우Country
). Dapper는 자동으로 분리 할 ID 라는 열을 찾습니다. 그러나 찾지 못하면splitOn
이 제공되지 않으면System.ArgumentException
이 유용한 메시지와 함께 표시됩니다. 따라서 옵션이지만 일반적으로splitOn
값을 제공splitOn
합니다.
일대 다 매핑
일대 다 관계가 포함 된보다 복잡한 예제를 살펴 보겠습니다. 이제 쿼리에 중복 데이터가 포함 된 여러 행이 포함될 것이므로이를 처리해야합니다. 우리는 클로저에서 조회를 수행합니다.
예제 클래스처럼 쿼리가 약간 변경됩니다.
신분증 | 이름 | 타고난 | CountryId | 나라 이름 | 도서 정보 | 도서 이름 |
---|---|---|---|---|---|---|
1 | 다니엘 데닛 | 1942 | 1 | 아메리카 합중국 | 1 | 브레인 스토밍 |
1 | 다니엘 데닛 | 1942 | 1 | 아메리카 합중국 | 2 | 팔꿈치 방 |
2 | 샘 해리스 | 1967 년 | 1 | 아메리카 합중국 | 삼 | 도덕적 풍경 |
2 | 샘 해리스 | 1967 년 | 1 | 아메리카 합중국 | 4 | 깨어 난다 : 종교없이 영성에 대한 안내 |
삼 | 리차드 도킨스 | 1941 | 2 | 영국 | 5 | 현실의 마법 : 우리가 어떻게 알았 는가 진짜인가요? |
삼 | 리차드 도킨스 | 1941 | 2 | 영국 | 6 | 경이로운 식욕 : 과학자 만들기 |
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; }
}
remainingHorsemen
사전은 인간 개체의 완전 구체화 된 인스턴스로 채워집니다. 쿼리 결과의 각 행에 대해 lambda 인수에 정의 된 유형의 인스턴스에 매핑 된 값이 전달되며이를 처리하는 방법은 사용자가 결정합니다.
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");
splitOn
인수가 다음 유형의 첫 번째 열에 대해 쉼표로 구분 된 목록splitOn
참고하십시오.
7 가지 이상의 매핑
가끔 매핑하는 유형의 수가 Func <>에서 제공하는 7을 초과합니다.
Query<>
를 제네릭 형식 인수 입력과 함께 사용하는 대신 배열로 매핑 할 형식과 매핑 함수를 제공합니다. 초기 수동 설정 및 값 캐스팅 이외의 나머지 기능은 변경되지 않습니다.
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");
사용자 지정 매핑
쿼리 열 이름이 클래스와 일치하지 않으면 유형에 대한 매핑을 설정할 수 있습니다. 이 예제는 사용자 지정 매핑뿐만 아니라 System.Data.Linq.Mapping.ColumnAttribute
를 사용하여 매핑하는 방법을 보여줍니다.
매핑은 유형별로 한 번만 설정하면되므로 응용 프로그램 시작시 또는 한 번만 초기화되는 다른 위치에 설정하면됩니다.
일대 다 예제와 같은 쿼리를 다시 가정하면 클래스가 다음과 같이 더 나은 이름을 향해 리팩토링됩니다.
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; }
}
Book
이ColumnAttribute
의존하지 않는 방법에ColumnAttribute
하지만if
문을 유지해야if
.
이제이 매핑 코드를 한 번만 실행되는 응용 프로그램의 어딘가에 배치하십시오.
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);
그런 다음 쿼리는 이전 Query<>
예제 중 하나를 사용하여 실행됩니다.
이 대답 에는 매핑을 추가하는 간단한 방법이 나와 있습니다.