Craig
Craig

Reputation: 18684

Linq and returning types

My GUI is calling a service project that does some linq work, and returns data to my GUI. However, I am battling with the return type of the method. After some reading, I have this as my method:

public static IEnumerable GetDetailedAccounts()
 {
 IEnumerable accounts =(from a in Db.accounts
                        join i in Db.financial_institution on  
                            a.financial_institution.financial_institution_id
                               equals i.financial_institution_id
                        join acct in Db.z_account_type on a.z_account_type.account_type_id 
                               equals acct.account_type_id
                        orderby i.name
                        select new 
                             {
                              account_id = a.account_id, 
                              name = i.name, 
                              description = acct.description
                              });
            return accounts;
}

However, my caller is battling a bit. I think I am screwing up the return type, or not handling the caller well, but it's not working as I'd hoped.

This is how I am attempting to call the method from my GUI.

IEnumerable accounts = Data.AccountService.GetDetailedAccounts();

Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Accounts:");
Console.ForegroundColor = ConsoleColor.White;

foreach (var acc in accounts)
  {
   Console.WriteLine(string.Format("{0:00} {1}", acc.account_id, acc.name + " " + acc.description));
  }

int accountid = WaitForKey();

However, my foreach, and the acc - isn't working. acc doesn't know about the name, description and id that I setup in the method. Am I at least close to being right?

Upvotes: 4

Views: 142

Answers (3)

Rami Alshareef
Rami Alshareef

Reputation: 7140

Davy8 is correct, and also creating a custom BusinessObject (AccountDetail) would be a better solution for long term external library life cycle (if you are using this AccountDetail alot in your logic/Code), since you can customize it as you want
having a transfer class (AccountDetail) easily allows you to bind back to some UI in design time and make the concept of your project a more bit clear, you can also re-use it more often

public class AccountDetail
{
public string AccountID{get;set;}
public string AccountName {get;set;}
public string AccountDescription {get;set}

public AccountDetail(){}
}

so Query is:

public static List<AccountDetail> GetDetailedAccounts()
{
  return (from a in Db.accounts
         join i in Db.financial_institution on a.financial_institution.financial_institution_id
equals i.financial_institution_id
join acct in Db.z_account_type on a.z_account_type.account_type_id equals
acct.account_type_id
orderby i.name
select new AccountDetail 
{   AccountID = a.account_id, 
     AccountName= i.name, 
      AccountDescription= acct.description}

}).ToList();

Upvotes: 0

BrokenGlass
BrokenGlass

Reputation: 160852

The problem is that you are using an anonymous type that the user of the method has no way of casting back to. An easy way out (besides reflection or creating a class) would be using dynamic, since you know the properties you want to access are there:

Example: (very made up I admit)

public static IEnumerable GetAccounts()
{
    return Enumerable.Range(0, 10)
           .Select(x => new { Id = x, Name = "herbert" });
}

//...
foreach (dynamic account in GetAccounts())
{
    Console.WriteLine(string.Format("Id: {0}, Name: {1}", 
                      account.Id, 
                      account.Name));
}

Back to your code that would mean just changing the calling code:

foreach (dynamic acc in accounts)
{
    Console.WriteLine(string.Format("{0:00} {1}", acc.account_id, acc.name + " " + acc.description));
}

Upvotes: 1

David Ly
David Ly

Reputation: 31586

@BrokenGlass is correct that the root of the issue is that you're using an anonymous type. That results in you using the non-generic version of IEnumerable which doesn't know what type the objects are.

Rather than using dynamic though (which requires .NET 4.0 which may or may not be an issue for you), I would recommend creating an actual Account class.

That way you can return an IEnumerable<Account> instead of just IEnumerable. Then you'll be able to use the properties since it knows that it's an Account.

Once you create the Account class your method then becomes:

public static IEnumerable<Account> GetDetailedAccounts()
{
    IEnumerable<Account> accounts = (from a in Db.accounts
                    join i in Db.financial_institution on a.financial_institution.financial_institution_id
                        equals i.financial_institution_id
                    join acct in Db.z_account_type on a.z_account_type.account_type_id equals
                        acct.account_type_id
                    orderby i.name
                    select new Account {account_id = a.account_id, name = i.name, description = acct.description});
    return accounts;

}

Then the calling code can remain the same.

I thought I should point out that var doesn't work the way you probably think it does. The type of the variable is still determined compile-time, which means (for the most part) it's just a shortcut. Your example would be identical to if you had used object instead of var because IEnumerable can only contain object.

If you're working in .NET I'd recommend follow .NET naming conventions though so AccountId instead of account_id but that won't affect whether it works or not.

Upvotes: 4

Related Questions