Giải pháp này chủ yếu dựa vào giải pháp từ @pius. Tôi muốn thêm tùy chọn hỗ trợ các tham số truy vấn để giúp giảm thiểu việc chèn SQL và tôi cũng muốn đặt nó thành một tiện ích mở rộng của DbContext DatabaseFacade cho Entity Framework Core để làm cho nó tích hợp hơn một chút.
Đầu tiên, hãy tạo một lớp mới với phần mở rộng:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace EF.Extend
{
public static class ExecuteSqlExt
{
/// <summary>
/// Execute raw SQL query with query parameters
/// </summary>
/// <typeparam name="T">the return type</typeparam>
/// <param name="db">the database context database, usually _context.Database</param>
/// <param name="query">the query string</param>
/// <param name="map">the map to map the result to the object of type T</param>
/// <param name="queryParameters">the collection of query parameters, if any</param>
/// <returns></returns>
public static List<T> ExecuteSqlRawExt<T, P>(this DatabaseFacade db, string query, Func<DbDataReader, T> map, IEnumerable<P> queryParameters = null)
{
using (var command = db.GetDbConnection().CreateCommand())
{
if((queryParameters?.Any() ?? false))
command.Parameters.AddRange(queryParameters.ToArray());
command.CommandText = query;
command.CommandType = CommandType.Text;
db.OpenConnection();
using (var result = command.ExecuteReader())
{
var entities = new List<T>();
while (result.Read())
{
entities.Add(map(result));
}
return entities;
}
}
}
}
}
Lưu ý ở trên rằng "T" là loại cho kết quả trả về và "P" là loại tham số truy vấn của bạn sẽ thay đổi tùy theo nếu bạn đang sử dụng MySql, Sql, v.v.
Tiếp theo chúng tôi sẽ đưa ra một ví dụ. Tôi đang sử dụng khả năng MySql EF Core, vì vậy chúng tôi sẽ xem cách chúng tôi có thể sử dụng tiện ích mở rộng chung ở trên với triển khai MySql cụ thể hơn này:
//add your using statement for the extension at the top of your Controller
//with all your other using statements
using EF.Extend;
//then your your Controller looks something like this
namespace Car.Api.Controllers
{
//Define a quick Car class for the custom return type
//you would want to put this in it's own class file probably
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public string DisplayTitle { get; set; }
}
[ApiController]
public class CarController : ControllerBase
{
private readonly ILogger<CarController> _logger;
//this would be your Entity Framework Core context
private readonly CarContext _context;
public CarController(ILogger<CarController> logger, CarContext context)
{
_logger = logger;
_context = context;
}
//... more stuff here ...
/// <summary>
/// Get car example
/// </summary>
[HttpGet]
public IEnumerable<Car> Get()
{
//instantiate three query parameters to pass with the query
//note the MySqlParameter type is because I'm using MySql
MySqlParameter p1 = new MySqlParameter
{
ParameterName = "id1",
Value = "25"
};
MySqlParameter p2 = new MySqlParameter
{
ParameterName = "id2",
Value = "26"
};
MySqlParameter p3 = new MySqlParameter
{
ParameterName = "id3",
Value = "27"
};
//add the 3 query parameters to an IEnumerable compatible list object
List<MySqlParameter> queryParameters = new List<MySqlParameter>() { p1, p2, p3 };
//note the extension is now easily accessed off the _context.Database object
//also note for ExecuteSqlRawExt<Car, MySqlParameter>
//Car is my return type "T"
//MySqlParameter is the specific DbParameter type MySqlParameter type "P"
List<Car> result = _context.Database.ExecuteSqlRawExt<Car, MySqlParameter>(
"SELECT Car.Make, Car.Model, CONCAT_WS('', Car.Make, ' ', Car.Model) As DisplayTitle FROM Car WHERE Car.Id IN(@id1, @id2, @id3)",
x => new Car { Make = (string)x[0], Model = (string)x[1], DisplayTitle = (string)x[2] },
queryParameters);
return result;
}
}
}
Truy vấn sẽ trả về các hàng như:
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"
Tiêu đề hiển thị không được xác định là một cột cơ sở dữ liệu, vì vậy nó sẽ không phải là một phần của mô hình Xe hơi EF theo mặc định. Tôi thích cách tiếp cận này như một trong nhiều giải pháp khả thi. Các câu trả lời khác trên trang này đề cập đến các cách khác để giải quyết vấn đề này với trình trang trí [NotMapped], tùy thuộc vào trường hợp sử dụng của bạn có thể là cách tiếp cận phù hợp hơn.
Lưu ý rằng mã trong ví dụ này rõ ràng là dài dòng hơn mức cần thiết, nhưng tôi nghĩ rằng nó làm cho ví dụ rõ ràng hơn.