# Friday, November 06, 2009

I realize this is a bit late, but as of just about 4 weeks ago I’m now the Architect at Eid Passport.  I’m really looking forward to building some cool stuff, with a team that’s had quite a bit of experience in this space. 

In short, Eid Passport makes perimeter security systems for secure facilities, and manages access to those facilities by vendors.  For example, say you are the Coke delivery guy at a military base.  It’s a hassle to get through the gate every time you deliver, since they have to make sure it’s OK for you to be on the base.  Now the Coke guy has the option of going to our kiosk at the base and signing up for an access card that will allow him to spend much less time getting in and out.  We do some background checks, employment verification (does he still really work for Coke, etc.) and then issue a credential that he can use to get through the gate.  Now Coke delivery guy can make N more deliveries in a day because he’s not spending time at the gate.  This is a new domain for me, so there’s a lot to learn, but it’s pretty exciting stuff.  All the way from a handheld scanner that reads all kinds of cards to back end access control and data processing servers. 

If any of that sounds interesting to you, we’re looking for some additional developers.  There are instructions on the website (see above) for how to submit your resume if you are interested.

Friday, November 06, 2009 9:32:28 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, June 17, 2009

We’ve added some new classes to the SoftSource training calendar, including a one-day Blend 2 class, and SQL 2008 for Developers.  Other offerings coming up are “Agile in a Day”, WPF, WCF, ASP.NET 3.5, LINQ, Silverlight, and of course, C#.  Discounts available for multiple students from the same organization, and custom on-site training also.

Wednesday, June 17, 2009 10:19:27 AM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, June 11, 2009

A month or so ago I posted on a solution for simulating “default button” semantics in a Silverlight app, meaning that if you are entering text in a text box and you hit the enter key, the “default button” for the “page” should be pressed.  Very natural for form entry, etc. 

An issue came up (discovered by John Papa) with the solution in a Prism app, because my solution depends on being able to find the “default” button in the visual tree using the FindName method.  That means that you have to be high enough up the visual tree to find the button, since it only works “down” the tree.  In a Prism app, it’s not necessarily clear where “high enough” might be.  Plus, because the solution requires unique names, and Prism modules may have nothing to do with one another, they may have duplicate names, etc.

Here’s a revision to the solution that doesn’t require unique names, and doesn’t require any static references that might interfere with proper garbage collection…

First, a new object called DefaultButtonHub that keeps track of the relationship between text boxes and buttons.  It also exposes an Attached Property that takes a DefaultButtonHub reference so we can hook up text boxes and buttons to the “hub” in XAML.

public class DefaultButtonHub
{
   ButtonAutomationPeer peer = null;

   private void Attach(DependencyObject source)
   {
       if (source is Button)
       {
           peer = new ButtonAutomationPeer(source as Button);
       }
       else if (source is TextBox)
       {
           TextBox tb = source as TextBox;
           tb.KeyUp += OnKeyUp;
       }
       else if (source is PasswordBox)
       {
           PasswordBox pb = source as PasswordBox;
           pb.KeyUp += OnKeyUp;
       }
   }

   private void OnKeyUp(object sender, KeyEventArgs arg)
   {
       if(arg.Key == Key.Enter)
           if (peer != null)
               ((IInvokeProvider)peer).Invoke();
   }

   public static DefaultButtonHub GetDefaultHub(DependencyObject obj)
   {
       return (DefaultButtonHub)obj.GetValue(DefaultHubProperty);
   }

   public static void SetDefaultHub(DependencyObject obj, DefaultButtonHub value)
   {
       obj.SetValue(DefaultHubProperty, value);
   }

   // Using a DependencyProperty as the backing store for DefaultHub.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty DefaultHubProperty =
       DependencyProperty.RegisterAttached("DefaultHub", typeof(DefaultButtonHub), typeof(DefaultButtonHub), new PropertyMetadata(OnHubAttach));

   private static void OnHubAttach(DependencyObject source, DependencyPropertyChangedEventArgs prop)
   {
       DefaultButtonHub hub = prop.NewValue as DefaultButtonHub;
       hub.Attach(source);
   }

}

Basically we’re expecting that both the text boxes and the button will register themselves with the “hub”.  If it’s a button that’s being registered, we wrap it in a ButtonAutomationPeer so we can “press” it later.  If it’s a text box, we hook up a KeyUp handler that will “press” the button if it’s there.  The requirement in the XAML is only marginally heavier than in my previous solution…we have to add a resource of type DefaultButtonHub, and point the button and text boxes at it using the {StaticResource} markup extension.

<UserControl x:Class="DefaultButton.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:my="clr-namespace:DefaultButton"
    Width="400" Height="300">
    <UserControl.Resources>
        <my:DefaultButtonHub x:Key="defaultHub"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBox x:Name="theText" Grid.Row="0"
                 my:DefaultButtonHub.DefaultHub="{StaticResource defaultHub}"/>
        <Button x:Name="theButton" Grid.Row="1" Content="Default"
                Click="theButton_Click" my:DefaultButtonHub.DefaultHub="{StaticResource defaultHub}"/>
    </Grid>
</UserControl>

Note that the new DefaultHub attached property is applied to both the text box and the button, each pointing the the single resource.  This way everything gets wired up property, there isn’t any problem with name resolution (aside from the usual resource name scoping) and everything will get cleaned up if the form needs to be GC’d.

Thursday, June 11, 2009 12:03:22 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, June 05, 2009

Last time, I talked about how to build a Domain Service from scratch using whatever POCO you have lying around.  Now it’s time to talk about how that works on the client side…

One of the coolest things about RIA Services is that you don’t even have to “Add Service Reference…” to get a reference to the Domain Service.  If your Silverlight project is linked to the ASP.NET project correctly (see the RIA Services doc for how this works) the build steps will take care of generating the right code in your Silverlight project, and away you go.  There are several ways of accessing the service client-side, from dead-easy to a bit more involved.  We’ll start with dead easy. 

The very easiest way to get things hooked up is to use the DomainDataSource control.  It wraps your DomainDataContext (the client-side generated bit) with a data source you can bind directly against in XAML.

<ria:DomainDataSource x:Name="PeopleDataSource" LoadMethodName="LoadPersons" AutoLoad="True">
  <ria:DomainDataSource.DomainContext>
      <services:PeopleDomainContext/>
  </ria:DomainDataSource.DomainContext>
</ria:DomainDataSource>
<dataControls:DataForm x:Name="dfPeople" CanUserAddItems="True" CanUserDeleteItems="true"
                     ItemsSource="{Binding ElementName=PeopleDataSource, Path=Data}" 
                     ItemEditEnded="dfPeople_ItemEditEnded">
</dataControls:DataForm>

The LoadMethodName attribute on the DomainDataSource tells it what method on the DomainContext to call in order to correctly populate the data context.  You can also pass parameters defined in XAML to the Load method if you only need to load a subset.  The DataForm control is bound to the Data property of the DomainDataSource, and away you go.  You get this

image

Because the Insert/Update/Delete methods are implemented on the server-side DomainService, the DataForm automagically enables the edit, add and delete buttons at the top.  If I edit or add a record, the save button shows up…

image

Pressing either the Save or Cancel button fires the ItemEditEnded event, which we can grab to submit the changes back to the server

private void dfPeople_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
{
  if (e.EditAction == DataFormEditAction.Commit)
      PeopleDataSource.SubmitChanges();
}

this is the very simplest case.  Calling SubmitChanges() here will send the edits/inserts up to the server right away.  For the sake of bandwidth, etc. I might want to implement my own “Save” button that batches up a whole set of change to the server rather than committing each edit individually.  You would still call PeopleDataSource.SubmitChanges, but not in response to the DataForm’s events. 

One of the great things about the way this works on the client side is that way the data validation attributes we set on the server-side POCO objects get propagated to the client.  For example, the server side LastName property looks like this (at least in the metadata class…)

[Required]
[RegularExpression("[a-zA-z]*")]
public string LastName;

The property that gets generated on the client side is

[DataMember()]
[RegularExpression("[a-zA-z]*")]
[Required()]
public string LastName
{
  get
  {
      return this._lastName;
  }
  set
  {
      if ((this._lastName != value))
      {
          this.ValidateProperty("LastName", value);
          this.RaiseDataMemberChanging("LastName");
          this._lastName = value;
          this.RaiseDataMemberChanged("LastName");
      }
  }
}

It maintains the [Required] and [RegularExpression] attributes.  Plus, in the property setter, it calls ValidateProperty, which uses reflection to examine those attributes and throw validation exceptions if necessary.  By default, then, I get UI on the Silverlight client for validation.

image

The DataForm provides the UI around both the field in question and the summary at the bottom.  In this case, I probably don’t want to tell the user what RegEx I’m validating against, so I need to add the ErrorMessage property to the validation attribute

[Required]
[RegularExpression("[a-zA-z]*", ErrorMessage="Alpha characters only please!")]
public string LastName;

image

When that error is corrected and I press the Save button, the Custom Validation routine is applied (see last post for details)

image 

When all the errors are corrected, and the save happens (and gets committed back to the server) the DataForm is updated to show the correct ID value (from the server) as well as the calculated Age property.

image

For next to no work, I get a pretty good user experience for browsing, editing, inserting and deleting records.  If I wanted a bit more control, I could load the data into the DataContext myself, and then setup the databinding, rather than using the DomainDataSource in XAML

private PeopleDomainContext _context = new PeopleDomainContext();

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
  if (_context.Persons.Count == 0)
  {
      _context.Loaded += (s, arg) => loadPeople();
      _context.LoadPersons();
  }
  else
      loadPeople();
}

private void loadPeople()
{
  dfPeople.ItemsSource = _context.Persons;
}

This would give me an opportunity to only load some people based on parameters, or whatever else I wanted to do to affect the loading of data before the data binding happens. 

Next time… more cool things you can do with the DomainDataContext

Friday, June 05, 2009 2:47:59 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, June 02, 2009

Most of the demos/samples I’ve looked at so far for RIA Services have started with a LINQ to SQL or ADO.NET Entity model and generated Domain Service classes from those.  I decided to start from something super simple (a POCO, if you will) and work up from there.  I started with a canonical  “Person” class

public partial class Person : INotifyPropertyChanged
{
   #region INotifyPropertyChanged Members

   public event PropertyChangedEventHandler PropertyChanged;

   #endregion

   protected virtual void Changed(string propertyName)
   {
       PropertyChangedEventHandler handler = PropertyChanged;
       if (handler != null)
       {
           handler(this, new PropertyChangedEventArgs(propertyName));
       }
   }

   private int _personId;
   public int PersonId
   {
       get
       {
           return _personId;
       }
       set
       {
           if (_personId != value)
           {
               _personId = value;
               Changed("PersonId");
           }
       }
   }

   private string _firstName;
   public string FirstName
   {
...
   }
   private string _lastName;
   public string LastName
   {
...
   }
   private System.DateTime _birthDate;
   public System.DateTime BirthDate
   {
...
   }
}

Nothing to see here.  It’s just a simple POCO that supports INotifyPropertyChanged for databinding.  Note that it’s a partial class…  The simplest path would be to add RIA Services attributes directly to these properties for things like data validation, but I wanted to try out all the features, so I split out the metadata into another file

[MetadataTypeAttribute(typeof(RiaServices.Web.Person.PersonMetadata))]
public partial class Person
{

   internal sealed class PersonMetadata
   {
       [Key]
       [Required]
       public int PersonId;

       [Required]
       [RegularExpression("[a-zA-z]*")]
       public string FirstName;

       [Required]
       [RegularExpression("[a-zA-z]*")]
       public string LastName;

       [Required]
       public DateTime BirthDate;
   }
}

This is kind of an interesting trick, and allows me to separate out all the RIA specific metadata from the original class definition.  This makes total sense when you look at the way they handle LINQ to SQL, for example.  In that case you already have Entity classes defined by the LINQ to SQL wizard, so this extra metadata class allows you to associate the right attributes without touching the (generated) LINQ to SQL classes.  Clever.  Notice that the metadata class uses fields, not properties, and just matches the names for the sake of simplicity.

In this case, the additional metadata defines data validation rules that get enforced both server and client side.  There are other attributes to enforcing string length and ranges. 

This all works because RIA Services generates “proxy” entity classes on the client side that are Silverlight compilable and also DataContracts (for serializing, which is cool…).  However, what happens if I have a calculated property that’s not just storage?  There’s a solution for that too, and it involves another piece of the partial class

public partial class Person
{
   [Shared]
   public int Age
   {
       get
       {
           this.Changed("Age");
           return Convert.ToInt32(Math.Floor((DateTime.Now - _birthDate).Days / 365.25));
       }
   }

}

This bit goes in a file called Person.shared.cs, and it will be copied to the client project and compiled there as well as on the server.  The [Shared] attribute marks the bits that need to be thus propagated.  Again, clever.  Of course, any such shared code has to compile in Silverlight.

The other piece of code I want to share (using the same method) is a custom validator.  In addition to the [Required] or [RegularExpression] attributes used above, you can register a custom validation routine that can examine the state of the entity as a whole.  The validation routine looks like this

[Shared]
public class PersonValidator
{
   public static bool IsPersonValid(Person p, ValidationContext context, out ValidationResult result)
   {
       bool valid = true;

       result = null;

       if (p.Age > 130)
           valid = false;

       if (!valid)
       {
           result = new ValidationResult("Birthdate is invalid, people can't be that old", new []{"BirthDate"});
       }

       return valid;

   }
}

That’s in a file called PersonValidator.shared.cs, so that it will be available client and server-side.  It’s associated with the Person entity with an additional attribute

[CustomValidation(typeof(PersonValidator), "IsPersonValid")]
[MetadataTypeAttribute(typeof(RiaServices.Web.Person.PersonMetadata))]
public partial class Person
...

With the Person entity all ready, I can expose it to the client by creating a new DomainService class with methods for Get, Insert, Update, Delete, etc.

[EnableClientAccess()]
public class PeopleDomainService : DomainService
{
   public IQueryable<Person> GetPersons()
   {

       return PeopleData.Persons.AsQueryable<Person>();
   }

   public IQueryable<Person> GetPerson(int personId)
   {
       return (from p in PeopleData.Persons
               where p.PersonId == personId
               select p).AsQueryable<Person>();
   }

   public void UpdatePerson(Person person)
   {
       Person oldP = (from p in PeopleData.Persons
                      where p.PersonId == person.PersonId
                      select p).FirstOrDefault();

       if (oldP != null)
       {
           PeopleData.Persons.Remove(oldP);
           PeopleData.Persons.Add(person);
       }
   }

   public void InsertPerson(Person person)
   {
       if (person.PersonId == 0)
       {
           int max = PeopleData.Persons.Max(p => p.PersonId);
           person.PersonId = max + 1;
       }

       PeopleData.Persons.Add(person);
   }

   public void DeletePerson(Person person)
   {
       Person oldP = (from p in PeopleData.Persons
                      where p.PersonId == person.PersonId
                      select p).FirstOrDefault();

       if (oldP != null)
       {
           PeopleData.Persons.Remove(oldP);
       }
   }
}

PeopleData.Persons in this case is a List<Person> that’s populated with some sample data.  The [EnableClientAccess] attribute causes the build-time bits to generate a client side proxy for calling the service without the client project needing a service reference.  It really makes the Silverlight and the Web projects feel like parts of the same app rather than disconnected pieces. 

The corresponding class that is generated on the client side is a DomainDataContext, which feels much like a LINQ to SQL DataContext only it’s lazy-loaded like the Astoria ones.  The GetPersons method on the server results in LoadPersons on the client, etc.  If I hadn’t implemented the Insert/Update/Delete methods on the server side, the DomainDataContext would simple behave like a read only data source.  This model works really well with the DataForm class.  If I set the ItemsSource of the DataForm to the Persons “table” in the client side data context, it will properly enable/disable the add/delete buttons depending on the capabilities of the data context.  Neat. 

Coming in future posts… hooking up the PersonDataContext to the Silverlight 3 UI, and in ASP.NET

Tuesday, June 02, 2009 3:53:18 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, May 28, 2009

Shaun and I will be doing a one-day training, “Practical Silverlight 3”, in lovely San Francisco, CA on July 8th.  It’s a full day of Silverlighty goodness, starting with the basics and movin’ on up.  We like to think of it as a 300 level technical introduction to the subject, so you will come away with a better understanding of the fundamentals, and where to look next.

Topics include

  • An introduction to Silverlight 3
  • All about controls (styling, templating, building your own)
  • Integrating with the browser
  • Communicating with the server via WCF, REST/POX, Sockets, you name it

If you are or will be in NorCal, come check it out.  It’s a good introduction for a very reasonable price.

Thursday, May 28, 2009 4:38:16 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, May 26, 2009

SoftSource developer Prashant Sinha is going to be presenting on “Production Debugging for Silverlight and ASP.NET” at Code Camp.  Prashant is a debugging wiz, and teaches our two-day .NET debugging course, so this should be a very useful session. 

Tuesday, May 26, 2009 1:51:11 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, May 21, 2009

In my book, I talked a bit about programming by contract and how that makes everyone’s lives easier.  Say I have a method that divides one integer by another

public double Divide(int dividend, int divisor)
{
  return dividend / divisor;
}

I’d like to be able to let callers know that they can’t pass a 0 for the divisor, because that will result in a DivideByZeroException, and nobody wants that.  In the past I had a couple of choices on how to express that, mostly involving writing different kids of code, because C# doesn’t have a native expression of design by contract like Eiffel does.  One way is to use Debug.Assert

public double Divide(int dividend, int divisor)
{
  Debug.Assert(divisor != 0);

  return dividend / divisor;
}

That way any caller that passes a 0 will get an assertion dialog at runtime, which brings it pretty dramatically to everyone’s attention.  The assumption is that using the Debug.Assert will flush out all cases where people are incorrectly calling my method during development, so it’s OK that the assertion will get compiled out of my Release build.  However, that doesn’t make it impossible for a caller to pass a 0 at runtime and cause the exception.  Another option is explicitly checking parameters and throwing a different exception.

public double Divide(int dividend, int divisor)
{
  if (divisor == 0)
      throw new ArgumentException("divisor cannot be zero", "divisor");

  return dividend / divisor;
}

Patrick argues that this has now moved from defining contract to defining behavior, and I can agree with that, although I’d probably argue that it defines both contract and behavior since I’ve extended the functionality of the Debug.Assert to the release build, while also protecting my internal state from bad data.  But that’s really a separate discussion… :)

Now thanks to the Microsoft Code Contracts project, I have a third option.  The Code Contracts project is the evolution of the work done on Spec#, but in a language neutral way.  The Code Contracts tools are currently available from DevLabs for VS 2008, as well as shipping with VS 2010 B 1.  Just at the moment, there are more features in the DevLabs version that what made it into the 2010 beta.  With Code Contracts, I can rewrite my Divide method like this

public double Divide(int dividend, int divisor)
{
  Contract.Requires(divisor != 0);

  return dividend / divisor;
}

I like the syntax, as it calls out quite explicitly that I’m talking about contract, and making an explicit requirement.  The default behavior of this method at runtime is identical to Debug.Assert, it brings up an assertion dialog and brings everything to a screeching halt. However, it’s configurable at build time, so I can have it throw exceptions instead, or do whatever might be appropriate for my environment if the contract is violated.  I can even get the best of both worlds, with a generic version of Requires that specifies an exception

public double Divide(int dividend, int divisor)
{
  Contract.Requires<ArgumentException>(divisor != 0, "divisor");

  return dividend / divisor;
}

I could configure this to bring up the assertion dialog in Debug builds, but throw ArgumentNullException in Release builds.  Good stuff.

The example above demonstrates a required “precondition”.  With Code Contracts, I can also specify “postconditions”. 

public void Transfer(Account from, Account to, decimal amount)
{
  Contract.Requires(from != null);
  Contract.Requires(to != null);
  Contract.Requires(amount > 0);
  Contract.Ensures(from.Balance >= 0);

  if (from.Balance < 0 || from.Balance < amount)
      throw new InsufficientFundsException();

  from.Balance -= amount;
  to.Balance += amount;

}

This isn’t the greatest example, but basically the Transfer method is promising (with the Contract.Ensures method) that it won’t ever leave the Balance a negative number.  Again, this is arguably behavior rather than contract, but you get the point. 

A really nifty feature is that I can write an interface definition and associate it with a set of contract calls, so that anyone who implements the interface will automatically “inherit” the contract validation.  The syntax is a bit weird, but you can see why it would need to be like this…

[ContractClass(typeof(ContractForCalucluator))]
interface ICalculator
{
   int Add(int op1, int op2);
   double Divide(int dividend, int divisor);
}

[ContractClassFor(typeof(ICalculator))]
class ContractForCalucluator : ICalculator
{
   #region ICalculator Members

   public int Add(int op1, int op2)
   {
       return default(int);
   }

   public double Divide(int dividend, int divisor)
   {
       Contract.Requires(divisor != 0);

       return default(double);
   }

   #endregion
}

Now any class that implements ICalculator will have the contract validated for the Divide method.  Cool.  The last thing I want to point out is that the team included a handy sample of how to work with contract validation in your MSTest unit test code.  The Contract class exposes an event called ContractFailed, and I can subscribe to the event to decide what happens on a failure.  For a test assembly, I can do this

[AssemblyInitialize]
public static void AssemblyInitialize(TestContext tc)
{
  Contract.ContractFailed += (sender, e) =>
  {
      e.SetHandled();
      e.SetUnwind();
      Assert.Fail(e.FailureKind.ToString() + " : " + e.Message);
  };
}

which will translate contract failures into test failures with very explicit error messages.  In the case of my Divide method if I run this test

[TestMethod()]
public void DivideContractViolation()
{
  Calculator target = new Calculator(); 
  int dividend = 12; 
  int divisor = 0; 
  double actual;
  actual = target.Divide(dividend, divisor);
  Assert.Fail("Should have failed");
}

I get a test failure of

Test method CalculatorTests.CalculatorTest.DivideContractViolation threw exception:  System.Diagnostics.Contracts.ContractException: Precondition failed: divisor != 0 divisor --->  Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.Fail failed. Precondition : Precondition failed: divisor != 0 divisor.

Cool.  This is definitely something I’ll be looking at more in the future.

Thursday, May 21, 2009 2:50:38 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, May 20, 2009

Not this weekend but next (5/30) is PDX Code Camp 2009.  A whole day of free software learning and general nerdy hanging out.  I’ll be talking about building business apps in Silverlight 3, and my SoftSource compadre Tim Johnson is going to be talking about making your web application more accessible for people with disabilities.  There are also a host of other sessions including some big names like Ward Cunningham, so it’s an event not to be missed, and cheap at twice the price!

Wednesday, May 20, 2009 1:45:43 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 

This came up in class yesterday, so I did a little digging.  Everyone may already know this, but it came as news to me. :)

If I’ve got a collection in memory, as well as a LINQ to SQL DataContext

List<FavoriteFood> foods = new List<FavoriteFood>(){
	new FavoriteFood{CustomerID = "ALFKI", Favorite="Chips"},
	new FavoriteFood{CustomerID = "ANATR", Favorite = "Fish"}};

NorthwindDataContext context = new NorthwindDataContext();

and I want to do an INNER JOIN between the foods list and the Customer table in Northwind, it would seem like this should do it

var bad = (from cust in context.Customers
         join f in foods on cust.CustomerID equals f.CustomerID
         select new
         {
             cust.ContactName,
             f.Favorite
         }).ToList();

but sadly, no.

image

if you stop to think about it, it totally makes sense that it wouldn’t work like that, since there’s no way to translate that into SQL in any rational way to send to SQL server. 

OK, so the next step would be to first get the customers in memory, then do the join

//this executes the whole query, thus retrieving the entire customer table
var customers = (from c in context.Customers
             select c).ToList();

//an inner join between the customers from SQL and the in-memory list
var inner = from cust in customers
     join f in foods on cust.CustomerID equals f.CustomerID
     select new
     {
         cust.ContactName,
         f.Favorite
     };

That works, but I’ve had to pull the entire Customer table into memory just to join two rows.  If I wanted to do a LEFT OUTER JOIN, I’d need that anyway, like so

//here's the left outer join between the customer list from SQL 
//and the in-memory favorites
var leftouter = from cust in customers
             join f in foods on cust.CustomerID equals f.CustomerID into custFoods
             from custFood in custFoods.DefaultIfEmpty()
             select new
             {
                 cust.ContactName,
                 Favorite = custFood == null ? null : custFood.Favorite
             };

but I want an inner join without pulling down all the customers, so I need to only fetch those rows that will join from Customer

//this is how you manage the IN clause
var x = from c1 in context.Customers
     where (from cf in foods
           select cf.CustomerID).Contains(c1.CustomerID)
     select c1;

//Note that to join the result in x back to the foods collection you would have to 
//execute the query just like with customers above...
var littleInnerJoin = from filteredCustomer in x.ToList()
                   join f in foods on filteredCustomer.CustomerID equals f.CustomerID
                   select new
                       {
                           filteredCustomer.ContactName,
                           f.Favorite
                       };

It’s two steps, but now I’ve just loaded the rows that will join into memory, and then done the join with LINQ to Objects.  It bears a little thinking about, but doesn’t seem like too much overhead, IMHO.

Work | LINQ
Wednesday, May 20, 2009 1:38:43 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |