MVC2 && EF4.0 Cascading Drop Down List

I am trying to create a cascading drop down in MVC2. This is a simple application so I am using Entity Framework 4.0 classes in my User Layer (instead of the usual POCOs).

I looked at Steve’s Walther’s solution(s) here: the problem is that it doesn’t work – you get an object null reference b/c the page isn’t done loading (I think). I tried putting it into JQuery, which I know waits to fire until the DOM is loaded, but I started getting too deep into the port.

I then looked at other people’s attempts like here and here – lots of code, little explanation and it doesn’t work without modifications.

I then went back to a project that I created does an Ajax call for the second drop down and tried to create my own solution.

Here is the initial controller:

1 public JsonResult GetDoctrinesForAnIssue(int issueId) 2 { 3 IDoctrineFactory doctrineFactory = new DoctrineFactory(); 4 List<Doctrine> doctrines = doctrineFactory.GetDoctrines(issueId); 5 return Json(doctrines.ToList()); 6 } 7  

The problem is that I am using an EF class directly in the JSON return. I am getting a missing object context because the entire graph is not loading and the context is disposed when it is going back to rehydrate – one of limitations of EF is that the instantiated class has to exist with the object context that created it so when that context goes away, you are stuck.

image

When I put the entire graph in to see, I got a circular reference error. Ugh

1 var doctrineQuery = (from doctrine in patentEntities.Doctrines 2 .Include("Issue") 3 .Include("Comment") 4 .Include("Comment.Claim") 5 .etc…. 6 select doctrine).ToList<Doctrine>(); 7 return doctrineQuery; 8  

I then thought about some of my other projects that I have worked on lately. I used POCOs extensively so this kind of problem didn’t surface – the downside is that I would then need to code up the POCOs, the Factories, and the mapping between the POCOs and EF classes – which is a bit overkill for this project. I then thought of a third way of using a dynamic object just for this drop down lists – outside of the factory, inside the controller:

1 public JsonResult GetDoctrinesForAnIssue(int issueId) 2 { 3 IDoctrineFactory doctrineFactory = new DoctrineFactory(); 4 var query = (from doctrine in doctrineFactory.GetDoctrines(issueId) 5 select new { doctrine.DoctrineId, doctrine.DoctrineName }).ToList(); 6 7 return Json(query); 8 } 9  

Add a little LINQ and boom goes the dynamite…

Advertisements

TDD using Interfaces

One of the tenants of TDD is that the tests are written, well, first.  The biggest problem I have with writing the tests first is one of developer productivity.  Call me a slave to tooling, but I really use intellisense when I code, so much to the point that I don’t even realize that I am using it.  With TDD, I can’t use Intellisense because the object has not been written yet.  As a way out of this problem, I thought of using interfaces.  I write the interface, write the test, then write the class under test.  I thought I would put my theory to the test with a basic example.  I created an interface like so:

1 using System; 2  using System.Collections.Generic; 3  using System.Linq; 4  using System.Text; 5  using Com.Tff.Patent.Data; 6 7  namespace Com.Tff.Patent.Domain 8 { 9 public interface IDoctrineFactory 10 { 11 static Doctrine GetDoctrine(int doctrineId); 12 static List<Doctrine> GetDoctrines(); 13 static List<Doctrine> GetDoctrines(int issueId); 14 } 15 } 16  

I then created a test like so:

1 [TestMethod] 2 public void GetDoctineReturnsCorrectValue() 3 { 4 IDoctrineFactory.GetDoctrine(1); 5 string expected = "aaa"; 6 string actual = doctrine.DoctrineName; 7 Assert.AreEqual(expected, actual); 8 9 } 10

However, I got a compiler error:

1 Error 1 The modifier 'static' is not valid for this item 2 3 Error 2 The modifier 'static' is not valid for this item 4 5 Error 3 The modifier 'static' is not valid for this item 6 7

That’s right – interfaces can’t be marked as static.  So then I thought of newing up a class in my test:

1 IDoctrineFactory doctrineFactory = new IDoctrineFactory(); 2 doctrineFactory.GetDoctrine(1); 3

The fact that I didn’t get intellisense was my first clue. The compiler error was my second:

1 Error 3 Cannot create an instance of the abstract class or interface

So then I created a concrete class using the refactoring tools in VS2010:

1 public class DoctrineFactory: IDoctrineFactory 2 { 3 public Data.Doctrine GetDoctrine(int doctrineId) 4 { 5 throw new NotImplementedException(); 6 } 7

This is better – I will get red b/c I have not implemented my methods yet – and I get intellisense in my unit tests. So for me, TDD is:

  • Write interface
  • Implement interfaces via VS2010
  • Write tests to get red
  • Write code logic to get to green

That way I get the intent behind TDD with modern tooling support.

Carpool Project: Part #15

I finished the carpool website – here is a quick summary of things I learned:

· Unit tests can’t depend on db data – refactoring violate data sucks

· I need to refactor my Unit Tests

· I need to learn more about MVC gators

· I need to learn more on LINQ

· Flattened POCOs are not needed in MVC

I also realized:

· Only having 5-6 custom categories in Live.Spaces sucks

· You need unique names for your photos in Live.Spaces. I was overwriting earlier posts’ pictures when I added new posts that had the same picture name

My next project will be taking the carpool project and making it a WM application.

Note that my Live.Spaces blog was moved to WordPress in October.  The formatting was so screwed up I just re-published the content.  I am using Live.Writer with a couple of plug-ins – which works well enough.

Carpool Project: Part #14

I had a new requirement pop up today. Seems that some users only want to see the carpools/practices that their kids are in – versus the entire set. Using my Alot based design, I wanted a function where the userName came in and the week’s carpools came out. I added the needed table (DriverSwimmer) to make the association between the user and swimmers and then though about my LINQ. I first coded up this solution:

1 public static List<Carpool> GetCarpools(DateTime startDate, DateTime endDate, string userName) 2 { 3 List<Carpool> carpools = new List<Carpool>(); 4 5 using (CarpoolEntities carpoolEntity = new CarpoolEntities()) 6 { 7 var carpoolQuery = (from carpool in carpoolEntity.Carpool_Carpool 8 .Include("Carpool_Driver") 9 .Include("Carpool_Driver.Carpool_DriverSwimmer") 10 .Include("Carpool_CarpoolSwimmer") 11 .Include("Carpool_CarpoolSwimmer.Carpool_Swimmer") 12 .Include("Carpool_CarpoolPractice") 13 .Include("Carpool_CarpoolPractice.Carpool_Practice") 14 .Include("Carpool_CarpoolPractice.Carpool_Practice.Carpool_SwimGroupPractice") 15 .Include("Carpool_CarpoolPractice.Carpool_Practice.Carpool_SwimGroupPractice.Carpool_SwimGroup") 16 .Include("Carpool_CarpoolPractice.Carpool_Practice.Carpool_SwimGroupPractice.Carpool_SwimGroup.Carpool_SwimTeam") 17 where carpool.CarPoolDate >= startDate 18 && carpool.CarPoolDate <= endDate 19 && carpool.Carpool_Driver.UserName == userName 20 orderby carpool.CarPoolDate 21 select carpool); 22 foreach (Carpool_Carpool carpool_carpool in carpoolQuery) 23 { 24 carpools.Add(MapCarpool(carpool_carpool)); 25 } 26 string s = EntityExtensionMethods.CustomExtensions.ToTraceString(carpoolEntity); 27 28 } 29 return carpools; 30 31 32 } 33  

The problem is that nothing is coming back. When I looked at the SQL being generated, it was null. I wonder if there are some phrases that are so complex that EF can’t figure it out. I thought of a great product/tool – you put in the sql you want and out comes the Linq. In any event, I decided to take a shortcut – I would get a large result set back and just parse it down for the user info.

1 var query = (from s in swimmers 2 select s.SwimGroup).Distinct(); 3 List<SwimGroup> swimGroups = query.ToList<SwimGroup>(); 4  

I combined to:

1 List<SwimGroup> swimGroups = ((from s in swimmers 2 select s.SwimGroup).Distinct()).ToList<SwimGroup>(); 3  

And then I wrote this:

1 var x = (from c in carpools 2 select c.Practices).ToList<Practice>(); 3  

and got the following error:

1 'System.Collections.Generic.IEnumerable<>' does not contain a definition for 'ToList' and the best extension method overload 'System.Linq.ParallelEnumerable.ToList<TSource>(System.Linq.ParallelQuery<TSource>)' has some invalid arguments

Apparently, ToList<>() does not have a ToList<>() method. I changed it to:

1 List<Practice> practices = (List<Practice>)(from c in carpools 2 select c.Practices); 3  

And the compiler was happy

I then completed my 1st attempt: note that I have 2 sets of swimgroups – those associated with the drivers (via their swimmers) and those associated with the practices (via their carpool):

1 public static List<Carpool> GetCarpools(DateTime startDate, DateTime endDate, string userName) 2 { 3 List<Swimmer> swimmers = DriverFactory.GetDriver(userName).Swimmers; 4 List<SwimGroup> driverSwimGroups = ((from s in swimmers 5 select s.SwimGroup).Distinct()).ToList<SwimGroup>(); 6 7 8 List<Carpool> carpools = GetCarpools(startDate, endDate); 9 List<Practice> practices = (List<Practice>)(from c in carpools 10 select c.Practices); 11 List<SwimGroup> practiceSwimGroups = (List<SwimGroup>)((from p in practices 12 select p.SwimGroups).Distinct()); 13 14 return null; 15 16 } 17  

 

I then needed to compare the swimGroup list – if they are in both, then the carpool should be returned like this:

1 List<SwimGroup> commonSwimGroups = (List<SwimGroup>)practiceSwimGroups.Union(driverSwimGroups);

However, I didn’t use this. I kludged up a 5-deep for..each nest of spaghetti code:

1 public static List<Carpool> GetCarpools(DateTime startDate, DateTime endDate, string userName) 2 { 3 List<Swimmer> swimmers = DriverFactory.GetDriver(userName).Swimmers; 4 List<SwimGroup> driverSwimGroups = ((from s in swimmers 5 select s.SwimGroup).Distinct()).ToList<SwimGroup>(); 6 7 List<Carpool> carpools = GetCarpools(startDate, endDate); 8 9 10 List<Carpool> returnValue = new List<Carpool>(); 11 foreach(Carpool carpool in carpools) 12 { 13 foreach (Practice practice in carpool.Practices) 14 { 15 foreach (SwimGroup practiceSwimGroup in practice.SwimGroups) 16 { 17 foreach (SwimGroup driverSwimGroup in driverSwimGroups) 18 { 19 if (practiceSwimGroup == driverSwimGroup) 20 { 21 returnValue.Add(carpool); 22 } 23 } 24 } 25 } 26 } 27 28 return returnValue; 29  

I have to believe there is a better way with some LINQ. I will investigate – until then, at least it is working…

Carpool Project: Part #13

I am closer to the end of the development cycle than the beginning and I have run into a new problem. I created a method that Inserts new practices in the PracticeFactory. When I test it against the server, I get the following error

1 Test method Com.Tff.Carpool.Domain.Tests.PracticeFactoryTest.InsertPracticeTest threw exception: 2 System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value. 3 The statement has been terminated. 4  

I searched MSDN/StackOverflow and I came up with the EF needing to be 2005, not 2008 (here) I switched it and got the same problem. This solution doesn’t make sense to me – b/c the data server is 2008. I then decided to look at the SQL that EF is generating to see if I can find an error in that code. There is a method called ToTraceString() that I can use to inspect the SQL generated – the trick is to locate what object the method belongs to – since it is not part of ObjectContext. The MSDN example here is not much help – I wonder if EF does not expose the method? In addition, I can’t set up tracing on the remote server – it is hosted on WinHost. I then found this post which solved the problem – it is just a feature gap in EF.

Once I had this code up and running, I looked at the results:

1 insert [dbo].[Carpool_Practice]([PracticeDate], [PracticeStartTime], [PracticeEndTime], [DrylandIndicator]) 2 values (@0, @1, @2, @3) 3 select [PracticeId] 4 from [dbo].[Carpool_Practice] 5  where @@ROWCOUNT > 0 and [PracticeId] = scope_identity() 6 @0 = 9/13/2010 7:17:53 AM 7 @1 = 1/1/0001 12:00:00 AM 8 @2 = 1/1/2010 7:00:00 PM 9 @3 = True 10  

And here is the bug:

1 private static void MapPractice_Practice(Carpool_Practice practiceToInsert, Practice practice) 2 { 3 practiceToInsert.DrylandIndicator = practice.DryLandIndicator; 4 practiceToInsert.PracticeDate = practice.Date; 5 practiceToInsert.PracticeEndTime = practice.EndTime; 6 practiceToInsert.PracticeStartTime = practiceToInsert.PracticeStartTime; 7 } 8  

I changed it to this:

1 practiceToInsert.PracticeStartTime = practice.StartTime;

And it ran. I was assigning the EF value to itself.

<Sound of me slapping my forehead>

Carpool Project: Part #12

I am working though the basic CRUD of EF/Factory and I ran into an issue. I coded

1 public static void DeleteCarpool(Carpool carpool) 2 { 3 using (CarpoolEntities carpoolEntity = new CarpoolEntities()) 4 { 5 var carpoolToDelete = (from cp in carpoolEntity.Carpool_Carpool 6 where cp.CarPoolId == carpool.Id 7 select cp).First(); 8 9 carpoolEntity.DeleteObject(carpoolToDelete); 10 carpoolEntity.SaveChanges(); 11 } 12 } 13  

 

And I got the following error in my unit test:

Carpool68

I then realized I didn’t cascade the changes on the server. I thought EF handled that for me – I guess not. I changed it in SQL Server:

Carpool69

And things ran like a champ….

Carpool Project: Part #11

I wanted to have a list of all of the swimmers NOT associated with a carpool. I got a list of all swimmers and then a list of all swimmers in carpool. I first started doing some nested For..Each and quickly realized I was doing something wrong. I went to 101 LINQ Samples and got a much better implementation (sample #52). I whipped this code:

1 public static List<Swimmer> GetNonCarpooledSwimmers(int carpoolId) 2 { 3 List<Swimmer> allSwimmers = SwimmerFactory.GetAllSwimmers(); 4 List<Swimmer> carpooledSwimmers = CarpoolFactory.GetCarpool(carpoolId).Swimmers; 5 return allSwimmers.Except(carpooledSwimmers).ToList<Swimmer>(); 6 } 7  

and ran a test:

Carpool67

Red! Dangnabbit.

I went to the definition of Except and realized that I did not implement a custom GetHashCode or Equals() for Swimmer. I popped this code into my Swimmer class:

1 2 public override int GetHashCode() 3 { 4 return this.Id; 5 } 6 7 public override bool Equals(object obj) 8 { 9 Swimmer other = obj as Swimmer; 10 if (other == null) 11 { 12 return base.Equals(obj); 13 } 14 else 15 { 16 if (other.Id == this.Id) 17 { 18 return true; 19 } 20 else 21 { 22 return false; 23 } 24 25 } 26 } 27  

 

And things ran like a champ…