Lambda and Let?

S

shapper

Hello,

I have the following:

return _context.Messages.Select(m => new Models.Message {
Id = m.Id,
Language = language,
Content = m.MessageLocalized.FirstOrDefault(l =>
l.Language.Code == language).Content,
Title = m.MessageLocalized.FirstOrDefault(l => l.Language.Code
== language).Title,
});

Is it possible to not have the two lines MessageLocalized and yet
using a lambda expression?

I think I could have a "let" but I am not sure how to use this with
lambda expressions.

Any idea?

Thanks,
Miguel
 
M

Martin Honnen

shapper said:
I have the following:

return _context.Messages.Select(m => new Models.Message {
Id = m.Id,
Language = language,
Content = m.MessageLocalized.FirstOrDefault(l =>
l.Language.Code == language).Content,
Title = m.MessageLocalized.FirstOrDefault(l => l.Language.Code
== language).Title,
});

Is it possible to not have the two lines MessageLocalized and yet
using a lambda expression?

I think I could have a "let" but I am not sure how to use this with
lambda expressions.

Can't you use a query expression e.g.

return (from m in _context.Messages
let ml = m.MessageLocalized.FirstOrDefault(l =>
l.LanguageCode.Code == language)
select new Models.Message {
Id = m.Id,
Language = language,
Content = ml).Content,
Title = ml.Title,
});
 
J

Jeroen Mostert

shapper said:
I have the following:

return _context.Messages.Select(m => new Models.Message {
Id = m.Id,
Language = language,
Content = m.MessageLocalized.FirstOrDefault(l =>
l.Language.Code == language).Content,
Title = m.MessageLocalized.FirstOrDefault(l => l.Language.Code
== language).Title,
});

Is it possible to not have the two lines MessageLocalized and yet
using a lambda expression?
Depends on what you mean by "lambda expression". This is still a lambda
expression, albeit a statement lambda rather than an expression lambda.

return
_context.Messages.Select(
m => {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
language);
return new Models.Message {
Id = m.Id,
Language = language,
Content = ml.Content,
Title = ml.Title
}
}
)
;

However, this seems contrived. I'd much rather use

var modelMessages =
from m in _context.Messages
let ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
language)
select new Models.Message {
Id = m.Id,
Language = language,
Content = ml.Content,
Title = ml.Title
}
;
return modelMessages;

If you want a proper functional language with "let" bindings in expressions,
you hopefully know where to get one. :)
 
S

shapper

Depends on what you mean by "lambda expression". This is still a lambda
expression, albeit a statement lambda rather than an expression lambda.

   return
     _context.Messages.Select(
       m => {
         var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
language);
         return new Models.Message {
           Id = m.Id,
           Language = language,
           Content = ml.Content,
           Title = ml.Title
         }
       }
     )
   ;

I get the error:
"A lambda expression with a statement body cannot be converted to an
expression tree"

On "m => {". I tried to solved it but now luck. Any idea?

However, this seems contrived. I'd much rather use

   var modelMessages =
     from m in _context.Messages
     let ml = m.MessageLocalized.FirstOrDefault(l => l.Language..Code ==
language)
     select new Models.Message {
       Id = m.Id,
       Language = language,
       Content = ml.Content,
       Title = ml.Title
     }
   ;
   return modelMessages;

Is it faster?
Yes, it looks more like T-SQL.

If you want a proper functional language with "let" bindings in expressions,
you hopefully know where to get one. :)

You mean F#? Isn't this only available in Net 4.0?

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
[...]
Depends on what you mean by "lambda expression". This is still a lambda
expression, albeit a statement lambda rather than an expression lambda.

return
_context.Messages.Select(
m => {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
language);
return new Models.Message {
Id = m.Id,
Language = language,
Content = ml.Content,
Title = ml.Title
}
}
)
;

I get the error:
"A lambda expression with a statement body cannot be converted to an
expression tree"

Sounds like the usual "LINQ to SQL" problem (i.e. your expressions have
to be mappable to SQL via an expression tree, and they aren't if you
have a statement body).
On "m => {". I tried to solved it but now luck. Any idea?

I don't think you can. Jeroen's answer was fine given the information
you chose to share. But apparently there's additional information not
shared by you relevant to the question that will prevent you from using
that syntax.
Is it faster?

Faster than what? The code you posted to start with? Sure...you only
call FirstOrDefault() once instead of twice.

One other thing: you seem to be setting yourself up for a
NullReferenceException. FirstOrDefault() can return "null" for
references types. You use the result unconditionally to access an
instance member. If the return value was "null", that will crash.

You should either check for a null return and do something usefully
different on null, or call First() instead of FirstOrDefault(), so that
the exception generated is at least more informative and in a more
useful place of execution.

Pete
 
T

Tim Roberts

shapper said:
You mean F#? Isn't this only available in Net 4.0?

My goodness, no! Where did you hear that? It works just fine with .NET
2.0. You can plug F# into either Visual Studio 2005 or 2008.
 
S

shapper

Sounds like the usual "LINQ to SQL" problem (i.e. your expressions have
to be mappable to SQL via an expression tree, and they aren't if you
have a statement body).

Sorry, I got a little bit lost ...

I can use Martin's suggestion ... No problem.
I just like to understand when something is not working, in this case
the "m => {", and if it can be solved rather then moving on to another
approach without fully understand what is wrong. Just that.
I don't think you can.  Jeroen's answer was fine given the information
you chose to share.  But apparently there's additional information not
shared by you relevant to the question that will prevent you from using
that syntax.

I will try to give full information.

I have 3 objects: Message, MessageLocalized and Language.
The three objects are mapped using LinqToSQL from SQL tables.

Basically each Message contains a list of MessageLocalized and each
MessageLocalized contains one Language:

Message messages = new Message();
messages.Language = new Language();
MessageLocalized messageLocalized1 = new MessageLocalized();
messages.SlideLocalized.Add(messageLocalized1);

Does this help?
One other thing: you seem to be setting yourself up for a
NullReferenceException.  FirstOrDefault() can return "null" for
references types.  You use the result unconditionally to access an
instance member.  If the return value was "null", that will crash.

You should either check for a null return and do something usefully
different on null, or call First() instead of FirstOrDefault(), so that
the exception generated is at least more informative and in a more
useful place of execution.

Thank you for the tip.
 
S

shapper

My goodness, no!  Where did you hear that?  It works just fine with .NET
2.0.  You can plug F# into either Visual Studio 2005 or 2008.

I had the idea that F# was still in a Beta version and would be fully
launched and integrated with VS 2010.
Now I see this already in CTP. I was reading about it. It seems nice
for example when dealing with math which is something I use.

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
Sorry, I got a little bit lost ...

I can use Martin's suggestion ... No problem.
I just like to understand when something is not working, in this case
the "m => {", and if it can be solved rather then moving on to another
approach without fully understand what is wrong. Just that.

Well, I've explained why that's not working. What about the explanation
left you "a little bit lost"?
I will try to give full information.

I have 3 objects: Message, MessageLocalized and Language.
The three objects are mapped using LinqToSQL from SQL tables.

Basically each Message contains a list of MessageLocalized and each
MessageLocalized contains one Language:

Message messages = new Message();
messages.Language = new Language();
MessageLocalized messageLocalized1 = new MessageLocalized();
messages.SlideLocalized.Add(messageLocalized1);

Does this help?

No. We are able to infer from your previous message in which you
described the error message that you are using LINQ to SQL. Telling us
now that you are using LINQ to SQL doesn't really add any new
information to the discussion.

Pete
 
S

shapper

Well, I've explained why that's not working.  What about the explanation
left you "a little bit lost"?






No.  We are able to infer from your previous message in which you
described the error message that you are using LINQ to SQL.  Telling us
now that you are using LINQ to SQL doesn't really add any new
information to the discussion.

I would addicional information if I could understand which one. :)

I will try again by explaining what I have and what I would like to
obtain:

public IQueryable<MessageModel> GetByLanguage(String languageCode) {

return _context.Messages.Select(m => {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
languageCode);
return new MessageModel {
Id = m.Id,
Name = m.Name,
Language = languageCode,
Content = ml.Content,
Title = ml.Title
}
});

};

I am trying to create a Flattered Object (MessageModel) from
properties of Entities Message, MessageLocalized and Language.

Given a languageCode I want to create a IQueryable<MessageModel> where
each MessageModel is defined by:

Id = Entity Message Id
Name = Entity Message Name
Language = languageCode or MessageLocalized.Language.Code (It will be
the same)
Content = Entity MessageLocalized Content
Title = Entity MessageLocalized Title

I understand the second suggestion Jeroen gave but I can't understand
why the first one gives me the error.

And I can't understand what other information I am missing to help in
solving that problem.

When I read:

return _context.Messages.Select(m => {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
languageCode);
return new MessageModel {
Id = m.Id,
Name = m.Name,
Language = languageCode,
Content = ml.Content,
Title = ml.Title
}
});

I interpret it as:

IList<MessageModels> result = new List<MessageModel>();
foreach (Message m in _context.Messages) {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
languageCode);
result.Add(new MessageModel {
Id = m.Id,
Name = m.Name,
Language = languageCode,
Content = ml.Content,
Title = ml.Title
}
}
return result;

So the part inside m => { is running the code inside the foreach loop
for each m.
But I don't understand why do I get the error and how can I solve it:
"A lambda expression with a statement body cannot be converted to an
expression tree"

Sorry that I am not so fast catching things as you :)

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
[...]
I understand the second suggestion Jeroen gave but I can't understand
why the first one gives me the error.

As I wrote before, it's a basic requirement of how LINQ to SQL works.
LINQ to SQL is translating your lambda expressions into SQL. This means
two things (at least):

-- It has to be a single expression, because the translation to SQL
is done via expression trees; a lambda that can't be converted to an
expression tree can't be used in LINQ to SQL

-- The expression has to be able to be rewritten as SQL

Your attempt to use a lambda statement body fails to meet the first
requirement. Even if C# allowed you to skip the first requirement, no
doubt it would fail to meet the second requirement. SQL has no
knowledge of your C# objects, such as whatever type the "ml" winds up being.
And I can't understand what other information I am missing to help in
solving that problem.

Solving what problem? Understanding why it won't work? Or getting it
to work? If the latter, then there is no amount of information you can
provide that will help solve that problem. There's no solution to that
problem. It simply cannot work that way.

If the former, the information you need to provide has nothing to do
with your code. Rather, for a better explanation to be provided, you
need to explain what it is about the explanations given so far that you
are having difficulty understanding.
When I read:

return _context.Messages.Select(m => {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
languageCode);
return new MessageModel {
Id = m.Id,
Name = m.Name,
Language = languageCode,
Content = ml.Content,
Title = ml.Title
}
});

I interpret it as:

IList<MessageModels> result = new List<MessageModel>();
foreach (Message m in _context.Messages) {
var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code ==
languageCode);
result.Add(new MessageModel {
Id = m.Id,
Name = m.Name,
Language = languageCode,
Content = ml.Content,
Title = ml.Title
}
}
return result;

That is an incorrect interpretation. In particular, LINQ in general
(and in this specific instance) does NOT actually execute a query or
enumeration until necessary. The thing that is returned in your first
example is the wrapper that, when eventually used, will retrieve the
data needed. The thing that is returned in your second example is
completely different, being the product of the actual work to do the
enumeration.

The other problem with your interpretation is that, even if the first
example did actually do the enumeration before returning from the
method, there's absolutely no way for the compiler to move the "ml = "
assignment out of the lambda. The lambda is an object unto itself and
having been passed to some other method, can be used at any time in any
way. Everything that is in the lambda as written in the code MUST
remain in the lambda, and that includes any local variables and their
initialization that are in the body of the lambda.
So the part inside m => { is running the code inside the foreach loop
for each m.
But I don't understand why do I get the error and how can I solve it:
"A lambda expression with a statement body cannot be converted to an
expression tree"

You can solve that error by writing the code as the second example
Jeroen provided (which is basically the same as the one Martin provided
from the outset).

Pete
 
S

shapper

shapper said:
[...]
I understand the second suggestion Jeroen gave but I can't understand
why the first one gives me the error.

As I wrote before, it's a basic requirement of how LINQ to SQL works.
LINQ to SQL is translating your lambda expressions into SQL.  This means
two things (at least):

     -- It has to be a single expression, because the translation to SQL
is done via expression trees; a lambda that can't be converted to an
expression tree can't be used in LINQ to SQL

     -- The expression has to be able to be rewritten as SQL

Your attempt to use a lambda statement body fails to meet the first
requirement.  Even if C# allowed you to skip the first requirement, no
doubt it would fail to meet the second requirement.  SQL has no
knowledge of your C# objects, such as whatever type the "ml" winds up being.
And I can't understand what other information I am missing to help in
solving that problem.

Solving what problem?  Understanding why it won't work?  Or getting it
to work?  If the latter, then there is no amount of information you can
provide that will help solve that problem.  There's no solution to that
problem.  It simply cannot work that way.

If the former, the information you need to provide has nothing to do
with your code.  Rather, for a better explanation to be provided, you
need to explain what it is about the explanations given so far that you
are having difficulty understanding.


When I read:
return _context.Messages.Select(m => {
  var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code==
languageCode);
    return new MessageModel {
      Id = m.Id,
      Name = m.Name,
      Language = languageCode,
      Content = ml.Content,
      Title = ml.Title
    }
});
I interpret it as:
IList<MessageModels> result = new List<MessageModel>();
foreach (Message m in _context.Messages) {
  var ml = m.MessageLocalized.FirstOrDefault(l => l.Language.Code==
languageCode);
  result.Add(new MessageModel {
    Id = m.Id,
    Name = m.Name,
    Language = languageCode,
    Content = ml.Content,
    Title = ml.Title
  }
}
return result;

That is an incorrect interpretation.  In particular, LINQ in general
(and in this specific instance) does NOT actually execute a query or
enumeration until necessary.  The thing that is returned in your first
example is the wrapper that, when eventually used, will retrieve the
data needed.  The thing that is returned in your second example is
completely different, being the product of the actual work to do the
enumeration.

The other problem with your interpretation is that, even if the first
example did actually do the enumeration before returning from the
method, there's absolutely no way for the compiler to move the "ml = "
assignment out of the lambda.  The lambda is an object unto itself and
having been passed to some other method, can be used at any time in any
way.  Everything that is in the lambda as written in the code MUST
remain in the lambda, and that includes any local variables and their
initialization that are in the body of the lambda.
So the part inside m => { is running the code inside the foreach loop
for each m.
But I don't understand why do I get the error and how can I solve it:
"A lambda expression with a statement body cannot be converted to an
expression tree"

You can solve that error by writing the code as the second example
Jeroen provided (which is basically the same as the one Martin provided
from the outset).

Pete

Thank you Pete for the explanation.

I thought that the problem was how my dbml problem was created ...
Now I see why this can't work with Linq To SQL.

I am using Jereon second example (or Martin's) and it is working fine.

Thank you Pete, Jereon, Martin and Tim :)
 
T

Tim Roberts

shapper said:
I had the idea that F# was still in a Beta version and would be fully
launched and integrated with VS 2010.
Now I see this already in CTP. I was reading about it. It seems nice
for example when dealing with math which is something I use.

It's well worth the trouble to learn F#, in my opinion. Learning the
philosophy is helping me to write better code in other languages.

However, I doubt I will ever write any real F# code. At the risk of
irritating the functional zealots, the language suffers from the same kind
of special-character disease that dooms Perl -- the syntax looks too much
like RS-232 line noise. It's a write-only language; two weeks after
writing something, I can't tell what it was supposed to do.

If those magical character sequences were written out in words, it would be
much more attractive to me. In a sense, that's what Python is becoming.
 

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

Top