我正在尝试通过查询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; }
}
)作为我正在处理的实际项目,该列有更多列,这将是意味着每次架构发生变化时这个查询都会中断(有点挫败了实体框架的目的)。
答案 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();