J'ai besoin d'utiliser Entity Framework, LINQ pour interroger datatables XML à partir du SQL dans mon application asp.net mvc (C #).
J'ai une colonne XMLValue
avec des données
<MetaData> <Reviews>1</Reviews> <Rating>1</Rating> </MetaData>
Je dois get tous les Customers
qui ont une Rating
de 1 à partir du xml. Je me suis référé à ce post de stackoverflow et je ne suis pas capable de l'atteindre.
J'ai ajouté la fonction SQL et l'ai ajoutée à mon edmx:
CREATE FUNCTION [dbo].[FilterCustomersByRating] (@Rating int) RETURNS TABLE AS RETURN SELECT XMLTest.* FROM XMLTest CROSS APPLY XMLValue.nodes('//MetaData') N(C) where NCvalue('Rating[1]', 'int')=@Rating GO
Et la fonction DB suivante:
[DbFunction("XMLDBModel.Store", "FilterCustomersByRating")] public static IQueryable<XMLTest> MyXmlHelper(int rating) { throw new NotImplementedException("You can only call this function in a LINQ query"); }
Ci-dessous est la requête linq que j'ai essayé exactement comme dans le post, mais pas en mesure d'utiliser la fonction et il jette une erreur.
var _dbCustomers = (from x in _context.XMLTests where MyXmlHelper(1).Where(xh=> xh.XMLValue.Contains("1")) select x);
Erreur:
Cannot implicitly convert type 'System.Linq.IQueryable<XMLTest>' to 'bool
Si j'utilise Any (), j'ai l'erreur suivante:
var _dbCustomers = (from x in _context.XMLTests where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1")) select x);
Erreur:
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 atsortingbute.
Quelqu'un peut-il suggérer sur la façon d'y parvenir s'il vous plaît?
Je pense que le problème est dû au type de return de votre fonction de rlocation.
Pouvez-vous vérifier quel est le type de return de votre méthode FilterCustomersByRating
dans votre DbContext? Je ne pense pas que ça devrait être XMLTest
. Cela devrait ressembler au code ci-dessous:
[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); }
Dans ce cas, le type de return de la fonction stub sera de type FilterCustomersByRating_Result
qui est généré automatiquement par la class lorsque vous ajoutez la fonction FilterCustomersByRating
Table à votre file edmx.
CREATE FUNCTION [dbo].[FilterCustomersByRating] (@Rating int) RETURNS TABLE AS RETURN SELECT XMLTest.* FROM XMLTest CROSS APPLY XMLValue.nodes('//MetaData') N(C) where NCvalue('Rating[1]', 'int')=@Rating GO
Dans cet esprit, votre fonction stub doit renvoyer 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"); }
vous pouvez l'utiliser comme indiqué ci-dessous:
var dbCustomers = (from x in _context.XMLTests where MyXmlHelper(1).Any(xh => xh.XMLValue.Contains("1")) select x);
S'il vous plaît noter que même si cela fonctionne, il returnnera tous les Customers
. Vous devrez peut-être modifier la fonction FilterCustomersByRating
pour accepter le CustomerID
et l' rating
.
Essaie.
MODIFIER
En plus de ce qui précède, lors de la définition de la fonction MyXmlHelper
MyXmlHelper , assurez-vous que l'orthographe de FunctionName
et NamespaceName
est correcte. Dans mon cas, le FunctionName
est FilterCustomersByRating
et NamespaceName
est TestingEntities
qui correspondent aux valeurs dans la class DBContext générée automatiquement.
// </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); } }
where
clause dans vos requêtes doit évaluer à une valeur bool
.
MyXmlHelper(1).Where(xh=> xh.XMLValue.Contains("1"))
donnera un logging de type System.Linq.IQueryable<XMLTest>
et non bool
. Vous devez find une expression qui returnnera une valeur bool
.
La même chose s'applique à la seconde erreur – changez votre clause where pour get la valeur bool
de l'expression.
CREATE FUNCTION [dbo].[FilterCustomersByRating] (@Rating int) RETURNS TABLE AS RETURN SELECT XMLTest.* FROM XMLTest CROSS APPLY XMLValue.nodes('//MetaData') N(C) where NCvalue('Rating', 'int') LIKE '<Rating>'.@Rating.'</Rating>' GO
Remplacez "=" par "LIKE". Je ne sais pas ce que N (C) est, cross apply, ou NCvalue (), mais en utilisant = au lieu de LIKE me donne souvent des ennuis. C'est en essayant d'évaluer ints / bools avec des strings, et pour des strings comme "1" vous devriez utiliser LIKE