views:

123

answers:

3

Hi all, I'm new to SubSonic and reasonably new to LINQ as well, so I'm just trying to put a little app together.

I've got the templates all sorted and running okay, but I've run into a bit of trouble with this LINQ statement (simplified slightly, the real statement has some other joins but they don't affect this particular problem so I've removed them for brevity):

var addresses = from address in Database.Addresses.All()
                           select new Address()
                               {
                                   MyNestedType = new NestedType()
                                       {
                                           Field1 = address.ADDR1
                                       }
                               };

If I execute this statement I get the error Invalid cast from 'System.String' to 'NestedType'. when I try to enumerate the results.

I'm probably overlooking the obvious but I can't see anywhere that I request such a conversion.

Both Field1 and address.ADDR1 are strings.

Any ideas what I'm doing wrong?

Edit:

I've had another look at this and in an effort to provide more information, I've created a small, complete example using SimpleRepository and an SQLite database that demonstrates the issue. Using SimpleRepository the error I get is different (Sequence contains no elements) but the result is the same. Here's the complete code:

 public class DatabaseAddress
 {
     public int Id { get; set; }
     public string Address1 { get; set; }
 }

 public class Address
 {
     public NestedType MyNestedType;
 }

 public class NestedType
 {
     public string Field1 { get; set; }
 }

 static class Program
 {
    [STAThread]
    static void Main()
    {
         var repo = new SimpleRepository("Db", SimpleRepositoryOptions.RunMigrations);
         DatabaseAddress address1 = new DatabaseAddress();
         address1.Address1 = "Test";
         repo.Add(address1);
         var all = repo.All<DatabaseAddress>();
         var addresses = from address in repo.All<DatabaseAddress>()
                         select new Address { MyNestedType = new NestedType { Field1 = address.Address1 } };
    }
 }

In this example, all contains the object added to the database, but addresses returns "Sequence contains no elements".

If I use anonymous types instead of concrete types in the select statement it works.

There's obviously a gap in my knowledge here; any help appreciated.

A: 

Try this

var nestedTypes= from address in Database.Addresses.All()
                select new NestedType()
                {
                 Field1 = address.ADDR1
                };
aikarads
That doesn't achieve what I'm after, which is one type nested inside another.
nukefusion
+2  A: 

Please see my question and answer here.

Here's how you can test if it is the same issue:

In that sample code you posted, change Field1 in your NestedType to be named Address1. Re-run your sample. If it works, same issue and the fix I answered with in the linked question should solve it for you.

qstarin
That doesn't seem to have fixed it in this case, but an interesting lead anyway which I'll look into, thanks.
nukefusion
This was almost right - MyNestedType also has to be renamed to match the class name, or vice-versa. Actually, I just tried it with Field1 unchanged, and it works, so just MyNestedType must be changed to match the class name (or the class renamed to MyNestedType).
Richard Hein
Ah crap, nevermind, it didn't work, now I get the original error : Invalid cast from 'System.String' to 'SubSonicTest.NestedType'. I forgot to change it back from an anonymous type. D'oh.
Richard Hein
+1  A: 

You have to call ToList(), otherwise the SubSonic provider tries to do something with MyNestedType and it doesn't exist in the database.

var addresses = from address in repo.All<DatabaseAddress>().ToList()
                        select new Address { MyNestedType = new NestedType { Field1 = address.Address1 } };

Update: It also works if you call ToList afterwards, i.e.:

addresses.ToList().ForEach(address => Console.WriteLine("Address.MyNestedType.Field1 = {0}", address.MyNestedType.Field1));

I guess there is a bug in the SubSonic query provider, because it does work for anonymous types, as you mentioned.

Richard Hein
That does it for me. I can now see where I was going wrong. Thanks.
nukefusion
Won't this defeat translation of where clauses and ordering, etc. to SQL because the entire table will be fetched and enumerated by calling ToList before any other translatable clauses are applied?
qstarin
Good point. I don't know the answer but presumably it would....
nukefusion
You can call ToList whenever it makes sense, just before the projection that is causing the issue: i.e.: var addresses = (from address in repo.All<DatabaseAddress>() where address.Id < 4 orderby address.Address1 select address) .ToList() .Select(address => new Address { NestedType = new NestedType { Address1 = address.Address1 } });
Richard Hein
Interesting, I guess I'll put this down to a Subsonic bug and restructure my code accordingly. Thanks for your help.
nukefusion