views:

323

answers:

5

Here is the code I'm working with, I'm still a bit new to LINQ, so this is a work in progress. Specifically, I'd like to get my results from this query (about 7 columns of strings, ints, and datetime), and return them to the method that called the method containing this LINQ to SQL query. A simple code example would be super helpful.

using (ormDataContext context = new ormDataContext(connStr))
{
    var electionInfo = from t1 in context.elections
               join t2 in context.election_status
               on t1.statusID equals t2.statusID
               select new { t1, t2 };
}

(In this case, my query is returning all the contents of 2 tables, election and election_status.)

A: 
return electionInfo.ToList();
Daniel A. White
The items selected by the query are of anonymous type. What return type do you sugges if using this return statement? Also, OP doesn't seem to require the data to be stored in a list. Converting to List AFAIK means eagerly reading all items, even if the client does not this. Why a list?
Jørn Schou-Rode
How would you define the List in the calling method? I tried and ran into issues with the List needing a type, but we only have an anonymous type(?)
alchemical
It must be a List or something like that because the data context is immedeately disposed, hence returning IQueryable or IEnumerable and evaluating the query later will result in an exception due to accessing a disposed object.
Daniel Brückner
but a List of what type?
alchemical
+1  A: 

You'll need to create classes that have the same structure as the anonymous types. Then, instead of "new { t1, t2 }", you use "new MyClass(t1, t2)".

Once you have a named class, you can pass it all over the place as you were hoping.

John Fisher
A: 

You cannot return an anonymous type from a method. It is only available within the scope in which it is created. You'll have to create a class instead.

Richard Hein
You can return it, but its compile time type will be object.
eulerfx
This seems like a very tedious way to deal with for every db call...
alchemical
@Eulerfx, yeah that's what I mean, the type information is gone. Unless you use reflection.
Richard Hein
@NagaMensch ... it's not that big of a deal. You just need a class with whatever properties you're anonymous type is returning ... automatic properties make it pretty quick.
Richard Hein
Do you really think having to create an object for every single db call is simple? A large project could easily have hundreds of db calls...then when the db changes the code is brittle and needs massive tedious changes.
alchemical
Whatmight be nice is if I could create a stored proc, drop it onto the LINQ design surface, and have it create an object from the sproc results that could then be easily used anywhere...this whole LINQ concept seems flawed -- tables are not objects, I don't need tables in my code, what I need is data, that is often in a radically different format than my tables. Tables are relational for storing the data, not for using it.
alchemical
Btw Eulerfx Richard, you mention you can return the var--I tried this and it doesn't seem to work at all. I'd be fine returning it as untyped, how would accomplish that? (var does not seem to show up in intellisense outside a local function)
alchemical
@NagaMensch: You don't return var ... you return an object from your method. Not IEnumerable<object>, just object. It's useless unless you do reflection on it. Using reflection would be considerable more brittle and time consuming when the DB changes, so it's not useful to do that in most cases.
Richard Hein
@NagaMensch: <quote>Do you really think having to create an object for every single db call is simple?</quote>Creating a class for an anonymous type shouldn't be required very often. You should have entities created for your application, one way or another. Creating classes "for each DB call" is simple if you use code generation. You can do that with the Entity Framework or whatever you want to use. If it's a large project you can't get around having to define your entities.
Richard Hein
+1  A: 

Specifically, I'd like to get my results from this query (about 7 columns of strings, ints, and datetime), and return them

Hi, the problem you've got with your query is that you're creating an anonymous type. You cannot return an anonymous type from a method, so this is where you're going to have trouble.

What you will need to do is to create a "wrapper" type that can take an election and an election_status and then return those.

Here's a little sample of what I'm talking about; as you can see I declare a Tuple class. The method that you will wrap your query in returns an IEnumerable.

I hope this helps :-)

class Tuple
{
    Election election;
    Election_status election_status;

    public Tuple(Election election, Election_status election_status)
    {
        this.election = election;
        this.election_status = election_status;
    }
}

public IEnumerable<Tuple> getElections()
{
    IEnumerable<Tuple> result = null;

    using (ormDataContext context = new ormDataContext(connStr))
    {
        result = from t1 in context.elections
                 join t2 in context.election_status
                 on t1.statusID equals t2.statusID
                 select new Tuple(t1, t2);
    }
}

UPDATE

Following from NagaMensch's comments, a better way to achieve the desired result would be to use the built in LINQ to SQL associations.

If you go to your entity diagram and click on toolbox, you will see 3 options. Class, Association and Inheritance. We want to use Association.

  • Click on Association and click on the ElectionStatus entity, hold the mouse button down and it will allow you to draw a line to the Election entity.

  • Once you've drawn the line it will ask you which properties are involved in the association. You want to select the StatusId column from the Election entity, and the StatusId column from the ElectionStatus entity.

Now that you've completed your mapping you will be able to simplify your query greatly because the join will not be necessary. You can just access the election status via a brand new property that LINQ to SQL will have added to the Election entity.

Your code can now look like this:

//context has to be moved outside the function
static ExampleDataContext context = new ExampleDataContext();

//Here we can return an IEnumerable of Election now, instead of using the Tuple class
public static IEnumerable<Election> getElections()
{
    return from election in context.Elections
           select election;
}

static void Main(string[] args)
{
    //get the elections
    var elections = getElections();

    //lets go through the elections
    foreach (var election in elections)
    {
        //here we can access election status via the ElectionStatus property
        Console.WriteLine("Election name: {0}; Election status: {1}", election.ElectionName, election.ElectionStatus.StatusDescription);
    }
}

You can also find a "how to" on LINQ to SQL associations here.

Note: It's worth mentioning that if you have an FK relationship set up between your tables in the database; LINQ to SQL will automatically pick the relationship up and map the association for you (therefore creating the properties).

DoctaJonez
I'll try it thx. My one concern is that this seems like a db table-center approach, whereas I'd like an architecture that gives me the freedom to query whatever results I need, i.e. they may not even come from a table, etc. So, I guess in that case I would be stuck creating perhaps dozens or even hundreds of these custom objects just to hold data result sets...
alchemical
New to LINQ and used this same method a few times.
dverespey
will this retain the integrity of the sql join?
alchemical
Indeed, you're correct. It would be a terrible idea to implement classes in this style each time you want to return results from a query. In LINQ to SQL you can tell the designer that there is a relationship between the two entities, and it will allow you to access the related entity via a property. That way you can return an Election and then access the status by calling election.election_status. This would be a much better way of doing this and would also maintain the integrity of your join.
DoctaJonez
I've updated the answer to explain how to use associations to implement a much better solution. If you get stuck at all or need further explanation just let me know :-) I hope this helps.
DoctaJonez
+1  A: 

The problem is, that you are creating a anonymous type, hence there is no way to declare a method with this return type. You have to create a new type that will hold your query result and return this type.

But I suggest not to return the result in a new type but return just a colection of election objects and access the election_status objects through the relation properties assuming you included them in your model. The data load options cause the query to include the related election status objects in the query result.

public IList<election> GetElections()
{
    using (ormDataContext context = new ormDataContext(connStr))
    {
        DataLoadOptions dlo = new DataLoadOptions();
        dlo.LoadWith<election>(e => e.election_status);
        context.DeferredLoadingEnabled = false;
        context.LoadOptions = dlo;   
        return context.elections.ToList();
    }
}

Now you can do the following.

IList<election> elections = GetElections();

// Access the election status.
Console.WriteLin(elections[0].election_status);

I general LINQ to SQL could just retrieve the related entities on demand - that is called deferred loading.

ormDataContext context = new ormDataContext(connStr));

IList<election> elections = context.elections.ToList();

// This will trigger a query that loads the election
// status of the first election object.
Console.WriteLine(elections[0].election_status);

But this requires you not to close the data context until you finished using the retrieved objects, hence cannot be used with a using statement encapsulated in a method.

Daniel Brückner
The other alternative is to create the context without the using. This way you won't need to call LoadWith numerous times (depending on the complexity of your query). The side effects of this are that the context won't be automatically disposed, and some queries will be deferred (elections.election_status).
Ryan Versaw
Good timing - just added deferred loading 23 seconds after your comment. ^^
Daniel Brückner
I noticed :) I also hadn't realized you could use LoadWith on the context - I could see that coming in handy. Thanks!
Ryan Versaw