views:

27

answers:

3

I am just getting my feet wet with LINQ to SQL, and need some advice in implementing a simple scenario:

I have a method ListTagCounts that is doing an aggregate query with LINQ To SQL. I was not sure of the return type to use, so used the ToList() method and created a class just for the return type in order to get it to compile.

Schema:

Link         LinkTag      Tag
----------   ----------   ----------
LinkID       LinkID       TagID
URL          TagID        Name
IsDeleted

Code:

static void Main(string[] args)
{
    var counts = ListTagCounts();
    foreach(var c in counts)
        Console.WriteLine("Tag: {0}, Count: {1}", c.Tag, c.Count);
    Console.ReadKey();
}

public static List<MyReturnClass> ListTagCounts()
{
    List<MyReturnClass> counts;
    using (var db = new MyDbDataContext())
    {
        counts = (from t in db.Tags
                  join lt in db.LinkTags on t.TagID equals lt.TagID
                  join l in db.Links on lt.LinkID equals l.LinkID
                  where l.IsDeleted == false
                  select new MyReturnClass() { Tag = t.Name, Count = t.LinkTags.Count() }
        ).Distinct().OrderBy(t => t.Tag).ToList();
    }
    return counts;
}

public class MyReturnClass
{
    public string Tag { get; set; }
    public int Count { get; set; }
}

If I do

select new { Tag = t.Name, Count = t.LinkTags.Count() }

rather than

select new MyReturnClass() { Tag = t.Name, Count = t.LinkTags.Count() }

in my LINQ query, what should the return type be?

+1  A: 

When you use the following syntax:

select new { Tag = ..., Count = ... };

You are creating an anonymous type. The compiler generates the name of this type for you at compile-time, so there is no way for you to know what it is.

When you call ToList on the query that selects instances of this anonymous type, it's possible that you can return the list because List implements some non-generic classes (which obviously don't rely on the type parameter T, which you aren't aware of) you can cast to.

Generally though, you should not let anonymous types escape the method that they are used in. If you have the need to pass the information outside of the method that the query is in, then what you are doing is correct, in creating a specialized class and then returning a sequence of that.

Note there are some justified uses for passing anonymous types outside of your method (through a return type of object, or other base classes for sequences of these types), but for cases like this, it isn't one of them.

casperOne
or wait for c# 4 and assign it to dynamic :-D
David Archer
@David...please don't do that...:D
Stan R.
@casperOne, i was talking to David, not to use `dynamic` for this.
Stan R.
@Stan R.: Fair enough, comment deleted. =)
casperOne
@casperOne +1 for a more reasonable explanation than mine, sometimes i get too lazy.
Stan R.
@Stan, yeah don't worry, was most definitely kidding :-)
David Archer
Thanks, that's helpful. My lack of experience with anonymous types is showing :)
RedFilter
A: 

In general you'd want to assign that to the 'var' type, but that's only good for local variables. 'object' maybe??? But that's a longshot

David Archer
no its an anonymous type, the compiler will generate a class definition on the fly for it and give it a random name, a name you have no way of knowing, this is why you use `var`
Stan R.
you're right, though I said you'd want to assign it to...
David Archer
+1  A: 

The first option will return an anonymous type which cannot be a return type of your method, the second way is a concrete class that holds your information, so the type is known and therefore is valid to be returned.

I would also suggest to return IEnumerable<MyReturnClass> unless you need it to be a list (read: you expect to add more items to it)

Stan R.
It is not clear to me how to use IEnumerable<MyReturnClass> in the query; when I do this I get *Cannot create an instance of the abstract class or interface 'System.Collections.Generic.IEnumerable<Linq2Sql_Test.Program.MyReturnClass>'*
RedFilter
@OrbMan: You don't use the anonymous type syntax in that case, you use `select new MyReturnClass() { Tag = ..., Count = ... }` as the select clause.
casperOne