具有自定义模型的实体框架Core Raw SQLQueries

pit*_*dis 2 entity-framework-core asp.net-core asp.net-core-2.0

使用Entity Framework 6,我能够使用执行原始SQL查询并使用未在DBContext中定义的自定义模型来存储查询的输出.一个简单的例子如下:

List<MyModel> data = context.Database.SqlQuery<MyModel>("SELECT Orders.OrderID, Customers.CustomerName FROM Orders INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;").ToList();
Run Code Online (Sandbox Code Playgroud)

我执行一个SQL命令,我期待一个自定义模型列表.

我尝试用Entity Framework Core做类似的事情,我发现的最接近的例子将迫使我从DBContext定义一个属性.这将不允许我使用自定义模型来填充SQL服务器将返回的数据.

var books = context.Books.FromSql("SELECT * FROM Books").ToList();
Run Code Online (Sandbox Code Playgroud)

此查询通知实体框架核心该查询将返回书籍列表.有没有办法在Entity Framework Core中实现这样的东西?

Mar*_*tín 10

从.NET Core 2.1开始:

  1. 添加modelBuilder.Query<YourModel>()OnModelCreating(ModelBuilder modelBuilder)

  2. 使用context.Query<YourModel>().FromSql(rawSql)获得的数据


pit*_*dis 5

问题是关于 .NET Core 2。现在我有了一个解决方案,我将把它写在这里,以便其他人可以在需要时使用它。

首先我们在dbContext类中添加如下方法

public List<T> ExecSQL<T>(string query)
{
    using (var command = Database.GetDbConnection().CreateCommand())
    {
        command.CommandText = query;
        command.CommandType = CommandType.Text;
        Database.OpenConnection();

        List<T> list = new List<T>();
        using (var result = command.ExecuteReader())
        {
            T obj = default(T);
            while (result.Read())
            {
                obj = Activator.CreateInstance<T>();
                foreach (PropertyInfo prop in obj.GetType().GetProperties())
                {
                    if (!object.Equals(result[prop.Name], DBNull.Value))
                    {
                        prop.SetValue(obj, result[prop.Name], null);
                    }
                }
                list.Add(obj);
            }
        }
        Database.CloseConnection();
        return list;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以有以下代码。

List<Customer> Customers = _context.ExecSQL<Customer>("SELECT ......");
Run Code Online (Sandbox Code Playgroud)

  • 这就是普通的 ADO.Net。并不是说它不起作用,而是我想知道为什么你要提出它,而 EF core 现在已经有内置的解决方案了。 (2认同)
  • 答案不值得投反对票=) 基本上,EF 中缺乏自定义查询支持。核心,我们在我们的架构中有外部视图,我们在其上执行联接和自定义查询,我们不能只添加自定义 DbSet,因为它不受支持,所以我们使用类似的东西,只是使用自动映射器和更好的缓存解决方案。此解决方案的问题是缺乏缓存,例如 getProperties 相当繁重,您可以用编译表达式替换 ActivatorCreateInstance。AutoMapper 基本上会完成所有肮脏的工作。 (2认同)

Mic*_*yen 5

这是我如何能够使其正常工作(为了完整性):

我的模型.cs:

public class MyModel
{
    // The columns your SQL will return
    public double? A { get; set; }
    public double? B { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

添加仅从原始 EF 上下文类继承的类(我称之为 DbContextBase):

public class DbContext : DbContextBase
{
    public virtual DbSet<MyModel> MyModels { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Necessary, since our model isnt a EF model
        modelBuilder.Entity<MyModel>(entity =>
        {
            entity.HasNoKey();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

使用该类(而不是您原来的 EF 上下文类):

// Use your new db subclass
using (var db = new DbContext())
{
    var models = await db.MyModels.FromSqlRaw(...).ToListAsync();    // E.g.: "SELECT * FROM apple A JOIN banana B ON A.col = B.col"
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 如果需要,只需使用FromSqlInterpolated代替 FromSqlRaw
  • “db context”子类允许您在不影响“polyfill”代码的情况下更新 EF 模型
  • 适用于仅返回 1 个结果集的 SQL Server 存储过程