具有重写列名称的原始查询

时间:2018-02-05 16:13:24

标签: c# sql-server entity-framework

我正在尝试通过查询XML列来使用Entity Framework检索一些实体。实体框架不支持这一点,所以我不得不使用原始SQL。

never

我的班级:

var people = context.People.SqlQuery("SELECT * FROM [People] WHERE [DataXML].value('Properties/Age', 'int') = 21").AsQueryable().AsNoTracking();

这应该可行,但是,当尝试将其映射回对象时,它会失效。具体来说,它落在了Age属性上,它的列名被覆盖为“YearsSinceBirth”。

  

'数据阅读器与指定的不兼容   'MyProject.CodeBase.DataModel.DbEntities.Person'。一个成员   type,'Age',数据读取器中没有相应的列   同名。'

我猜测实体框架没有将数据库列名称映射到对象属性名称,因此期望该列被命名为“Age”而不是“YearsSinceBirth”。

我不想在SQL查询中列出每个列及其映射(如public class Person { public int Id { get; set; } public string Name { get; set; } [Column("YearsSinceBirth")] public int Age { get; set; } [Column(TypeName = "xml")] public string DataXML { get; set; } } )作为我正在处理的实际项目,该列有更多列,这将是意味着每次架构发生变化时这个查询都会中断(有点挫败了实体框架的目的)。

2 个答案:

答案 0 :(得分:1)

如果这是EF Core,那么问题不在于SqlQuery()不支持映射列名(它确实如此)。相反,您的问题是您的表格中没有包含名为YearsSinceBirth的列,而您正在返回'选择*'。

如果您有一个名为YearsSinceBirth的列,这可以正常工作。虽然您将检索YearsSinceBirth列中的值,而不是XML文档中的值。 EG

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
//using Microsoft.Samples.EFLogging;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;

namespace EFCore2Test
{


    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }

        [Column("YearsSinceBirth")]
        public int Age { get; set; }

        [Column(TypeName = "xml")]
        public string DataXML { get; set; }
    }

    public class Location
    {
        public string LocationId { get; set; }
    }

    public class Db : DbContext
    {
        public DbSet<Person> People { get; set; }
        public DbSet<Location> Locations { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=(local);Database=EFCoreTest;Trusted_Connection=True;MultipleActiveResultSets=true");
            base.OnConfiguring(optionsBuilder);
        }
    }




    class Program
    {


        static void Main(string[] args)
        {

            using (var db = new Db())
            {
                db.Database.EnsureDeleted();
                //db.ConfigureLogging(s => Console.WriteLine(s));
                db.Database.EnsureCreated();

                var p = new Person()
                {
                    Name = "joe",
                    Age = 2,
                    DataXML = "<Properties><Age>21</Age></Properties>"
                };
                db.People.Add(p);
                db.SaveChanges();
            }
            using (var db = new Db())
            {
                var people = db.People.FromSql("SELECT * FROM [People] WHERE [DataXML].value('(/Properties/Age)[1]', 'int') = 21").AsNoTracking().ToList() ;

                Console.WriteLine(people.First().Age);

                Console.ReadLine();
            }

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

您可以使用与此类似的模式来投影XML或JSON列中的实体属性:

public class Person
{
    private XDocument xml;

    public int Id { get; set; }

    public string Name { get; set; }

    [NotMapped]
    public int Age
    {
        get
        {
            return int.Parse(xml.Element("Properties").Element("Age").Value);
        }
        set
        {
            xml.Element("Properties").Element("Age").Value = value.ToString();
        }
    }

    [Column(TypeName = "xml")]
    public string DataXML
    {
        get
        {
            return xml.ToString();
        }
        set
        {
            xml = XDocument.Parse(value);
        }
    }
}

答案 1 :(得分:0)

如果需要,您可以借助反射和ColumnAttribute检查动态创建带别名的选择查询:

public string SelectQuery<T>() where T : class
{
    var selectQuery = new List<string>();
    foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        var attr = prop.GetAttribute<ColumnAttribute>();
        selectQuery.Add(attr != null ? $"{attr.Name} as {prop.Name}" : prop.Name);
    }
    return string.Join(", ", selectQuery);
}

<强>用法:

var people = context.People.SqlQuery($"SELECT {SelectQuery<Person>()} FROM [People] WHERE [DataXML].value('Properties/Age', 'int') = 21")
                    .AsQueryable().AsNoTracking();