Linq Query

S

shapper

Hello,

I have the following Linq Query (_brands and _products are
XDocument's):

Product product = _products.Root.Elements("Product").Select(p =>
new Models.Product {
Id = Int32.Parse(p.Element("Id").Value),
Brand = _brands.Root.Elements("Brand").Select(b => new
Models.Brand {
Id = Int32.Parse(b.Element("Id").Value),
Name = b.Element("Name").Value
}).Where(b => b.Element("BrandId").Value == p.Element
("BrandId").Value).SingleOrDefault(),
//...

I get the error:
'Brand' does not contain a definition for 'Element' and no extension
method 'Element' accepting a first argument of type 'Brand' could be
found (are you missing a using directive or an assembly reference?)

I understand that the problem is in Where. But if I move the Where
before Select I always get problems.
How can I solve this?

And maybe I can avoid using Where and just use FirstOrDefault or
SingleOrDefault?

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
Hello,

I have the following Linq Query (_brands and _products are
XDocument's):

Product product = _products.Root.Elements("Product").Select(p =>
new Models.Product {
Id = Int32.Parse(p.Element("Id").Value),
Brand = _brands.Root.Elements("Brand").Select(b => new
Models.Brand {
Id = Int32.Parse(b.Element("Id").Value),
Name = b.Element("Name").Value
}).Where(b => b.Element("BrandId").Value == p.Element
("BrandId").Value).SingleOrDefault(),
//...

I get the error:
'Brand' does not contain a definition for 'Element' and no extension
method 'Element' accepting a first argument of type 'Brand' could be
found (are you missing a using directive or an assembly reference?)

I understand that the problem is in Where.

Not really. The problem is that you are trying to use a member method
named "Element()" in a class that doesn't have a member method named
"Element()".

The "Where()" method call is only incidental to your real problem.
But if I move the Where
before Select I always get problems.
How can I solve this?

Hard to say without a concise-but-complete code example.

Just looking at the code, it appears that you're misusing the XML from
the outset, and ought to be generating a Id-to-Brand mapping dictionary
from your data up-front, so you can use that inside your queries, rather
than enumerating the entire XML document each time you want to look up a
Brand by Id.

The code example you posted is difficult to understand, not the least
because it's incomplete. But the code also doesn't really make much
sense. You are creating a whole new Brand instance from scratch, based
on "Brand" elements in the XML, but then you are looking for a "BrandId"
element not in the XML, but in your Brand instances. This fails to make
sense for two reasons:

-- Brand is not an XElement. The Element() method isn't a member
of it.

-- "BrandId" appears to be the name given for the child element of
the "Product" XElement, so even if you had a "Brand" XElement to work
with, why would that be the proper name for the element you want to find?
And maybe I can avoid using Where and just use FirstOrDefault or
SingleOrDefault?

It's not clear from the code example why a "default" value is helpful at
all. Presumably if your data says a particular product has a particular
brand ID, that brand should also be in the dataset. Why not just make
it an error, catching the exception if First() throws one?

Beyond that, maybe getting rid of the Where() is useful, but you haven't
said what the value of it was presumed to be in the first place, so who
knows? Again, without a concise-but-complete code example, it's hard to
know for sure what you meant, but maybe something like this would work:

Product product = _products.Root.Elements("Product")
// are you missing a .Where() here to restrict the
// "Product"? that is, why project all of the
// "Product" elements, if you're only going to
// wind up with one new Model.Product instance in
// the end?
.Select(p => new Models.Product
{
Id = Int32.Parse(p.Element("Id").Value),
Brand = new Models.Brand
{
Id = p.Element("BrandId").Value,
Name = _brands.Root.Elements("Brand").
.Where(b => b.Element("Id").Value ==
p.Element("BrandId").Value)
.First().Element("Name").Value
}
}).First();

(You didn't even bother to post the complete query, so who knows what
you're really trying to accomplish here...I just filled it out with
something basic)

In other words, when you want to initialize a new Brand instance for
your new Product instance, and you need the value of the "Name" property
for that Brand instance, rather than mapping _all_ of the names for
_all_ of the brands, and then trying to select the appropriate brand
instance from that (whether an XElement or Brand), just get the name for
the one brand that's important.

I didn't bother to include it, but of course you may be able to improve
the performance more with a "let" in your query, to cache the value of
p.Element("BrandId").Value. I'm not sure, but I don't think that's
something the compiler could catch on its own, since it has no way to
guarantee that the Element() method returns an invariant, given the
invariant input.

And if that doesn't help, you really need to post a more specific
question with a clear, concise-but-complete code example.

Pete
 
S

shapper

Not really.  The problem is that you are trying to use a member method
named "Element()" in a class that doesn't have a member method named
"Element()".

The "Where()" method call is only incidental to your real problem.


Hard to say without a concise-but-complete code example.

Just looking at the code, it appears that you're misusing the XML from
the outset, and ought to be generating a Id-to-Brand mapping dictionary
from your data up-front, so you can use that inside your queries, rather
than enumerating the entire XML document each time you want to look up a
Brand by Id.

The code example you posted is difficult to understand, not the least
because it's incomplete.  But the code also doesn't really make much
sense.  You are creating a whole new Brand instance from scratch, based
on "Brand" elements in the XML, but then you are looking for a "BrandId"
element not in the XML, but in your Brand instances.  This fails to make
sense for two reasons:

     -- Brand is not an XElement.  The Element() method isn't a member
of it.

     -- "BrandId" appears to be the name given for the child element of
the "Product" XElement, so even if you had a "Brand" XElement to work
with, why would that be the proper name for the element you want to find?


It's not clear from the code example why a "default" value is helpful at
all.  Presumably if your data says a particular product has a particular
brand ID, that brand should also be in the dataset.  Why not just make
it an error, catching the exception if First() throws one?

Beyond that, maybe getting rid of the Where() is useful, but you haven't
said what the value of it was presumed to be in the first place, so who
knows?  Again, without a concise-but-complete code example, it's hard to
know for sure what you meant, but maybe something like this would work:

   Product product = _products.Root.Elements("Product")
     // are you missing a .Where() here to restrict the
     // "Product"?  that is, why project all of the
     // "Product" elements, if you're only going to
     // wind up with one new Model.Product instance in
     // the end?
     .Select(p => new Models.Product
       {
         Id = Int32.Parse(p.Element("Id").Value),
         Brand = new Models.Brand
           {
             Id = p.Element("BrandId").Value,
             Name = _brands.Root.Elements("Brand").
               .Where(b => b.Element("Id").Value ==
p.Element("BrandId").Value)
               .First().Element("Name").Value
           }
       }).First();

(You didn't even bother to post the complete query, so who knows what
you're really trying to accomplish here...I just filled it out with
something basic)

In other words, when you want to initialize a new Brand instance for
your new Product instance, and you need the value of the "Name" property
for that Brand instance, rather than mapping _all_ of the names for
_all_ of the brands, and then trying to select the appropriate brand
instance from that (whether an XElement or Brand), just get the name for
the one brand that's important.

I didn't bother to include it, but of course you may be able to improve
the performance more with a "let" in your query, to cache the value of
p.Element("BrandId").Value.  I'm not sure, but I don't think that's
something the compiler could catch on its own, since it has no way to
guarantee that the Element() method returns an invariant, given the
invariant input.

And if that doesn't help, you really need to post a more specific
question with a clear, concise-but-complete code example.

Pete

I am not sure if I understood everything on your post but my entire
code, that now compiles is:

Product product _products.Root.Elements("Product").Select(p =>
new Models.Product {
Id = Int32.Parse(p.Element("Id").Value),
Brand = _brands.Root.Elements("Brand").Where(b => b.Element
("BrandId").Value == p.Element("BrandId").Value).Select(b => new
Models.Brand {
Id = Int32.Parse(b.Element("Id").Value),
Name = b.Element("Name").Value
}).FirstOrDefault(),
Name = p.Element("Name").Value,
PhotoUrl = p.Element("PhotoUrl").Value
}).FirstOrDefault(p => p.Id == id);

Each Product node has a BrandId value and that is what I am using to
get the Brand's name so I can fill the Brand object.
 
A

Arne Vajhøj

shapper said:
I have the following Linq Query (_brands and _products are
XDocument's):

Product product = _products.Root.Elements("Product").Select(p =>
new Models.Product {
Id = Int32.Parse(p.Element("Id").Value),
Brand = _brands.Root.Elements("Brand").Select(b => new
Models.Brand {
Id = Int32.Parse(b.Element("Id").Value),
Name = b.Element("Name").Value
}).Where(b => b.Element("BrandId").Value == p.Element
("BrandId").Value).SingleOrDefault(),
//...

I get the error:
'Brand' does not contain a definition for 'Element' and no extension
method 'Element' accepting a first argument of type 'Brand' could be
found (are you missing a using directive or an assembly reference?)

I understand that the problem is in Where. But if I move the Where
before Select I always get problems.
How can I solve this?

And maybe I can avoid using Where and just use FirstOrDefault or
SingleOrDefault?

Not a specific reply to your question.

But as a general rule: if LINQ makes your code more difficult
to read and modify than more traditional code, then use more
traditional code !

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Linq. Where 1
Linq Query 5
Class and XML file. Very strange problem. 1
Get Child Nodes 3
Delete and Update 2
String Conversion 1
Linq Query on XML file. Please, what am I doing wrong? 2
Linq. String 5

Top