4

Linq query with default values. If in DB Table this values are not found, than default values from object should be taken, and later on new row will be added to this table.

It should go like this, but this does not work:

var name_country = (from m in ctx.person
                where (m.name == oPerson.name || m.country == oPerson.country)
                 select new 
                 {
                   m.name, m.country
                 }
                ).DefaultIfEmpty
                               (
                                 oPerson.name,
                                 oPerson.country
                               ).FirstOrDefault();

How to set this Default Values in DefaultIfEmpty???

New Edit: This is what I want to make as one query:

string name = (from m in ctx.person
                where (m.name == oPerson.name || m.country == oPerson.country)
                 select  
                   m.name
                ).DefaultIfEmpty
                               (
                                 oPerson.name,
                               ).FirstOrDefault();
string country = (from m in ctx.person
                where (m.name == oPerson.name || m.country == oPerson.country)
                 select 

                  m.country

                ).DefaultIfEmpty
                               (
                                 oPerson.country
                               ).FirstOrDefault();
3m1n4
  • 53
  • 1
  • 1
  • 8

3 Answers3

4
var name_country = (from m in ctx.person
            where (m.name == oPerson.name || m.country == oPerson.country)
             select new 
             {
               m.name, m.country
             }
            ).DefaultIfEmpty
                           (new {
                             oPerson.name,
                             oPerson.country
                           }).First();

This will work as long as the member-layout is identical.
This works, as anonymous types are anonymous at run-time at all ... Please read the MSDN-entry for more information on this topic:

If two or more anonymous object initializers in an assembly specify a sequence of properties that are in the same order and that have the same names and types, the compiler treats the objects as instances of the same type. They share the same compiler-generated type information.

besides I would rather go for a ?? ...

var name_country = (from m in ctx.person
                    where (m.name == oPerson.name || m.country == oPerson.country)
                    select new 
                    {
                        m.name,
                        m.country
                    }).FirstOrDefault() ?? new {
                        oPerson.name,
                        oPerson.country
                    };

edit: here's a working fiddle

James
  • 80,725
  • 18
  • 167
  • 237
  • How would you use `??` on the collection? – Patrick Hofman Aug 14 '14 at 09:42
  • after `.FirstOrDefault()` you could do a `??`, as the return-value of `.FirstOrDefault()` is `T` and not `IEnumerable` –  Aug 14 '14 at 09:42
  • This does not work. Tried it before posting question – 3m1n4 Aug 14 '14 at 09:43
  • What purpose does it serve if you check the collection already? – Patrick Hofman Aug 14 '14 at 09:43
  • @PatrickHofman I don't understand your question, please elaborate! –  Aug 14 '14 at 09:44
  • @AndreasNiedermair (I pesume you and Dirk speak German :-)): Fehler 49 "System.Linq.IQueryable" enthält keine Definition für "DefaultIfEmpty", und die Überladung der optimalen Erweiterungsmethode "System.Linq.ParallelEnumerable.DefaultIfEmpty(System.Linq.ParallelQuery, TSource)" weist einige ungültige Argumente auf. – 3m1n4 Aug 14 '14 at 10:04
  • @3m1n4 assumed right. nevertheless: you could change the exception translation by changing the UI culture of your executing user :) ... and: are you doing linq2sql or linq2ef? ore anything that is not managed? is the member-layout identical? –  Aug 14 '14 at 10:07
  • I am doing Linq2ef. And it is not. This is only a simplified version of what i realy have. – 3m1n4 Aug 14 '14 at 11:37
  • @AndreasNiedermair your Code in Fiddle looks good, but my VS shows the same Error. Thanks anyway! – 3m1n4 Aug 14 '14 at 12:13
  • @3m1n4 then I am strongly supsecting your setup, because the basic signature and features of the language support it (as shown in my fiddle) –  Aug 14 '14 at 12:15
  • I don't what to do any more :-(. I have to leave it because I have so much more to do and some other time I will try to make it work. Thank you @AndreasNiedermair (I will make your answer as correct, because it actualy is, just not in my context) and thanks to all of you guys. You are greate! – 3m1n4 Aug 14 '14 at 12:32
  • @3m1n4 your best chance with minimum effort: overcome anonymous types and use/create a simple dto-class or use `Tuple`... –  Aug 14 '14 at 12:56
1

You are looking for this overload of DefaultIfEmpty

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(
    this IEnumerable<TSource> source,
    TSource defaultValue
)

You should create a new anonymous object, set it properties, and pass it to the constructor.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • 4
    It's possible to work with anonymous types, e.g. `x.Select(p => new { X = 100 }).DefaultIfEmpty(new { X = 200 })` works. – Dirk Aug 14 '14 at 09:37
  • 1
    @Dirk interesting. I wouldn't expect it to work, how does the compiler infer the types ? type of two identical anonymous object should be different – Selman Genç Aug 14 '14 at 09:39
  • 2
    @Selman22 They are not different, as long as they are in the same assembly (maybe some other conditions apply as well). As so often, Eric Lippert has a blog post about that: http://blogs.msdn.com/b/ericlippert/archive/2012/01/23/anonymous-types-unify-within-an-assembly.aspx – Dirk Aug 14 '14 at 09:41
  • @Dirk it is all about member-layout, which includes: type, name, and even position (... as far as I remember) of members. There are no *real* anonymous types - .net is type-safe ... –  Aug 14 '14 at 09:54
  • @AndreasNiedermair yep, they have to be *exact*. – James Aug 14 '14 at 10:20
1

Assuming you have a Person class that looks like

public class Person
{
    public string Name { get; set; }
    public string Country { get; set; }
}

What you want to do here is create a new instance of Person (which will automatically set the default values for each particular property type) if one isn't returned from your DB query e.g.

var name_country = (from m in ctx.person
                    where (m.name == oPerson.name || m.country == oPerson.country)
                    select new Person
                    {
                        Name = m.name, 
                        Country = m.country
                    }).FirstOrDefault() ?? new { oPerson.name, oPerson.country };

Just realised that you want to default the fields from the oPerson instance rather than a new instance. So assuming oPerson is also an anonymous object with the exact same member structure, you could do

var name_country = (from m in ctx.person
                    where (m.name == oPerson.name || m.country == oPerson.country)
                    select new
                    {
                        m.name, 
                        m.country
                    })
                    .DefaultIfEmpty(aPerson)
                    .FirstOrDefault();
James
  • 80,725
  • 18
  • 167
  • 237
  • +1 Since I didn't think to use the null-coalescing operator which makes you code more compact and elegant. – Christos Aug 14 '14 at 09:38
  • are you sure, even if `oPerson` is of anonymous type, it has the same member-layout as the anonymous type which gets introduced in the select? –  Aug 14 '14 at 09:54
  • @Selman22 why wouldn't `select new Person` not work in EF? There isn't any CLI-specific projections going on here. @Andreas that's a good point actually, they would need to have the *exact* same structure, I'll make that prominent in the answer. – James Aug 14 '14 at 10:07
  • sorry I confused, it doesn't work [with constructor](http://stackoverflow.com/questions/3571084/only-parameterless-constructors-and-initializers-are-supported-in-linq-to-entiti) but the initializer is fine.. – Selman Genç Aug 14 '14 at 10:11
  • @James thanks ;) btw,`Enumerable.DefaultIfEmpty` only works on collections, not on single instances ... http://msdn.microsoft.com/de-de/library/bb355419(v=vs.110).aspx ... but I guess that's just a typo –  Aug 14 '14 at 10:12
  • @Selman22 that's a long outstanding issue with EF (not sure if it's been addressed in the later versions). However, it's a limitation whereby EF can't project classes that don't have parameterless constructors, in my case, `Person` will have. – James Aug 14 '14 at 10:19