使用Entity Framework Database First从SQL查询Xml

4

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

我有一个名为XMLValue的列,其中包含数据。

<MetaData>
     <Reviews>1</Reviews>
     <Rating>1</Rating>
</MetaData>

我需要从xml中获取所有评分为1的客户。我参考了此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

以下是一个数据库相关的函数:

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

以下是我尝试的linq查询,与帖子中完全相同,但无法使用该函数并且会抛出错误。

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

错误:

Cannot implicitly convert type 'System.Linq.IQueryable<XMLTest>' to 'bool

如果我使用Any(),那么会出现以下错误:
 var _dbCustomers = (from x in _context.XMLTests
                          where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1"))
                          select x);

错误:

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.

有人能提供一下如何实现这个的建议吗?


你是否正在编写SQL(结构化查询语言),并且真正意思是使用微软SQL Server(实际产品)?如果是,请添加“sql-server”标签以明确表达。如果不是:这是为了什么数据库系统? - marc_s
1
@marc_s,是的,我正在SQL Server中编写SQL查询(函数),并已更新标签。感谢您指出它 :) - Prasad
它会抛出什么错误?“您只能在LINQ查询中调用此函数”? - Typist
@Typist,它显示错误消息“无法隐式转换类型 'System.Linq.IQueryable<XMLTest>' 到 'bool'”。我还尝试过使用Any(),但是出现了“无法翻译”的错误。请检查我的更新的问题。 - Prasad
3个回答

5

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

您能否检查一下FilterCustomersByRating方法在您的DbContext中的返回类型是什么?我认为它不应该是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);
}

在这种情况下,存根函数的返回类型将是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

考虑到这一点,您的存根函数应该返回IQueryable<FilterCustomersByRating_Result>,即:
[EdmFunction("TestingDbEntities", "FilterCustomersByRating")]
public static IQueryable<FilterCustomersByRating_Result> MyXmlHelper(int rating)
{ 
    throw new NotImplementedException("You can only call this function in a LINQ query");
}

您可以按照下面的方式使用它:
var dbCustomers = (from x in _context.XMLTests
                   where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1"))
                   select x);

请注意,虽然这样做可以工作,但它将返回所有的Customers。您可能需要修改FilterCustomersByRating函数以接受CustomerIDrating
试试看。
编辑
除上述之外,在定义MyXmlHelper EdmFunction时,请确保FunctionNameNamespaceName的拼写正确。在我的情况下,FunctionNameFilterCustomersByRatingNamespaceNameTestingEntities,与自动生成的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);
    }
}

@Prasad,我已经编辑了答案以添加更多信息。让我知道你的进展如何。 - Jamleck
我之前使用的是Store命名空间。将其更改为DBContext的命名空间后,出现了错误“参数数据类型xml对于like函数的第1个参数无效。” - Prasad
在运行LINQ(引发上述错误)时生成的SQL如下所示:SELECT [Extent1].[XMLId] AS [XMLId], [Extent1].[XMLValue] AS [XMLValue] FROM [dbo].[XMLTest] AS [Extent1] WHERE EXISTS (SELECT 1 AS [C1] FROM [dbo].FilterXmlByRating AS [Extent2] WHERE [Extent2].[XMLValue] LIKE N'%1%' ) - Prasad
当您在SSMS上运行SQL语句时,是否出现错误?请查看此链接。它建议在将列传递给“like”之前将其转换为“nvarchar(max)”。这有帮助吗? - Jamleck
让我们在聊天中继续这个讨论 - Jamleck
显示剩余3条评论

2

第一个错误

您的查询中的where子句需要评估为bool值。

MyXmlHelper(1).Where(xh=> xh.XMLValue.Contains("1"))将返回类型为System.Linq.IQueryable<XMLTest>而不是bool。您需要想出一个表达式,它将返回一个bool值。

第二个错误

同样适用于第二个错误-更改您的where子句以从表达式获取bool值。


我不确定如何在LINQ的where子句中使用DbFunctions。我按照其他SO帖子(在我的问题中引用)所说的做了,但仍然没有成功。有关此事的任何帮助? - Prasad
你的数据库函数返回表格,它是否有CustomerId列? - Typist
是的。它将CustomerId作为主键。 - Prasad

1
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', 'int') LIKE '<Rating>'.@Rating.'</Rating>'
GO

将“=”替换为“LIKE”。不知道N(C)是什么,也不了解cross apply或N.C.value(),但使用=而不是LIKE经常会给我带来麻烦。它试图将ints / bools与字符串交叉评估,并且对于像“1”这样的字符串,应该使用LIKE。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接