使用Entity Framework Database First从SQL查询Xml

Pra*_*sad 4 c# xml linq sql-server entity-framework-6

我需要使用Entity Framework,LINQ在我的asp.net mvc(C#)应用程序中查询SQL中的XML数据.

我有一个XMLValue包含数据的列

<MetaData>
     <Reviews>1</Reviews>
     <Rating>1</Rating>
</MetaData>
Run Code Online (Sandbox Code Playgroud)

我需要从xml中获得所有Customers拥有Rating1的人.我已经提到了这个stackoverflow帖子,我无法实现它.

我添加了SQL函数并将其添加到我的edmx:

CREATE FUNCTION [dbo].[FilterCustomersByRating] 
    (@Rating int) 
RETURNS TABLE
AS 
RETURN
    SELECT XMLTest.*
    FROM XMLTest
    CROSS APPLY XMLValue.nodes('//MetaData') N(C)
    where N.C.value('Rating[1]', 'int')=@Rating
GO
Run Code Online (Sandbox Code Playgroud)

以及DB功能:

[DbFunction("XMLDBModel.Store", "FilterCustomersByRating")]
public static IQueryable<XMLTest> MyXmlHelper(int rating)
{
            throw new NotImplementedException("You can only call this function in a LINQ query");
}
Run Code Online (Sandbox Code Playgroud)

下面是我在帖子中完全尝试的linq查询,但是无法使用该函数并且它会抛出错误.

 var _dbCustomers = (from x in _context.XMLTests
                     where MyXmlHelper(1).Where(xh=> xh.XMLValue.Contains("1"))
                     select x);
Run Code Online (Sandbox Code Playgroud)

错误:

Cannot implicitly convert type 'System.Linq.IQueryable<XMLTest>' to 'bool
Run Code Online (Sandbox Code Playgroud)

如果我用户Any(),我有以下错误:

 var _dbCustomers = (from x in _context.XMLTests
                          where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1"))
                          select x);
Run Code Online (Sandbox Code Playgroud)

错误:

The specified method 'System.Linq.IQueryable`1[XMLTest] MyXmlHelper(Int32)' on the type 'CustomerRepository' cannot be translated into a LINQ to Entities store expression because its return type does not match the return type of the function specified by its DbFunction attribute.
Run Code Online (Sandbox Code Playgroud)

有人可以建议如何实现这一目标吗?

Jam*_*eck 5

我认为问题是由存根函数的返回类型引起的.

你能检查一下FilterCustomersByRatingDbContext中你的方法的返回类型吗?我认为不应该这样XMLTest.它应该类似于下面的代码:

[EdmFunction("TestingDbEntities", "FilterCustomersByRating")]
public virtual IQueryable<FilterCustomersByRating_Result> FilterCustomersByRating(Nullable<int> rating)
{
    var ratingParameter = rating.HasValue ?
        new ObjectParameter("Rating", rating) :
        new ObjectParameter("Rating", typeof(int));

    return ((IObjectContextAdapter)this)
    .ObjectContext
    .CreateQuery<FilterCustomersByRating_Result>("[TestingEntities]
        .[FilterCustomersByRating](@Rating)", ratingParameter);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,存根函数的返回类型将是类型FilterCustomersByRating_Result,当您将FilterCustomersByRating表值函数添加到edmx文件时,该类是自动生成的类.

CREATE FUNCTION [dbo].[FilterCustomersByRating] 
    (@Rating int) 
RETURNS TABLE
AS 
RETURN
    SELECT XMLTest.*
    FROM XMLTest
    CROSS APPLY XMLValue.nodes('//MetaData') N(C)
    where N.C.value('Rating[1]', 'int')=@Rating
GO
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,你的存根函数应该返回IQueryable<FilterCustomersByRating_Result> ie

[EdmFunction("TestingDbEntities", "FilterCustomersByRating")]
public static IQueryable<FilterCustomersByRating_Result> MyXmlHelper(int rating)
{ 
    throw new NotImplementedException("You can only call this function in a LINQ query");
}
Run Code Online (Sandbox Code Playgroud)

你可以使用它,如下所示:

var dbCustomers = (from x in _context.XMLTests
                   where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1"))
                   select x);
Run Code Online (Sandbox Code Playgroud)

请注意,虽然这将工作,它将返回所有Customers.您可能需要修改FilterCustomersByRating函数以接受CustomerIDrating.

试试看.

编辑

除了上面的,定义时MyXmlHelper EdmFunction,确保的拼写FunctionNameNamespaceName是正确的.在我的例子中,FunctionNameis FilterCustomersByRatingNamespaceNameTestingEntities匹配自动生成的DBContext类中的值.

// </auto-generated code>
public partial class TestingEntities : DbContext
{
    public TestingEntities()
        : base("name=TestingEntities")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public DbSet<XMLTest> XMLTests { get; set; }

    [EdmFunction("TestingEntities", "FilterCustomersByRating")]
    public virtual IQueryable<FilterCustomersByRating_Result> FilterCustomersByRating(Nullable<int> rating)
    {
        var ratingParameter = rating.HasValue ?
            new ObjectParameter("Rating", rating) :
            new ObjectParameter("Rating", typeof(int));

        return ((IObjectContextAdapter)this)
        .ObjectContext
        .CreateQuery<FilterCustomersByRating_Result>("[TestingEntities]
            .[FilterCustomersByRating](@Rating)", ratingParameter);
    }
}
Run Code Online (Sandbox Code Playgroud)