Linq does not (nor will it ever) change the fact that sometimes business logic is messy. There is no magic bullet that will materialize business rules. If there was, we wouldn't have a job. The truth of the matter is that a variable is being passed in that has to be checked for nullability. The Linq expression forms a closure over that variable and attempts to convert it to SQL which is NOT possible. Notice that query (since it is a reference variable) has a value greater than 1 (or true) and null does not (false). The expression gets evaluated that way. There is no way around it.
What your query would return would be all the entities with a null surname + all the entities that have the surname of the closed over variable.
Next, I believe I can create a reduction from Circuit SAT to your problem as you have set it up, since you have a number of variables and'ed and or'ed together and you want to find satisfiability. So if you do find a magic algorithm that solves your problem, then you have solved a problem that has eluded computer scientists for 50 years.
Finally, using the Predicate builder I was able to create the where clause to the linq expression just fine using the nullability checks. Here is the example:
public Expression<Func<VehicleEntity, bool>> RetrievePredicateExpression()
{
Expression<Func<VehicleEntity, bool>> predicate = PredicateBuilder.Null<VehicleEntity>();
if(!checkAllLocations.Checked)
predicate.And<VehicleEntity>(v => this.SelectedLocations.Contains(v.AssignedTo));
if (checkAssigned.Checked)
predicate = predicate.And<VehicleEntity>(v => v.EmployeeAssigned != null);
else
predicate = predicate.And<VehicleEntity>(v => v.EmployeeAssigned == null);
this.SelectedFuelTypes = _fuelTypeControl.GetCheckedItems();
if (this.SelectedFuelTypes != null && this.SelectedFuelTypes.Count > 0)
predicate = predicate.And<VehicleEntity>(v => this.SelectedFuelTypes.Contains(v.FuelTypeId));
this.SelectedLessors = _lessorControl.GetCheckedItems();
if (this.SelectedLessors != null && this.SelectedLessors.Count > 0)
predicate = predicate.And<VehicleEntity>(v => this.SelectedLessors.Contains(v.LessorId));
this.SelectedMakes = _makeControl.GetCheckedItems();
if (this.SelectedMakes != null && this.SelectedMakes.Count > 0)
predicate = predicate.And<VehicleEntity>(v => this.SelectedMakes.Contains(v.MakeId));
this.SelectedVehicleStatus = _vehicleStatusControl.GetCheckedItems();
if (this.SelectedVehicleStatus != null && this.SelectedVehicleStatus.Count > 0)
predicate = predicate.And<VehicleEntity>(v => this.SelectedVehicleStatus.Contains(v.VehicleStatusId));
this.SelectedVehicleTypes = _vehicleTypeControl.GetCheckedItems();
if (this.SelectedVehicleTypes != null && this.SelectedVehicleTypes.Count > 0)
predicate = predicate.And<VehicleEntity>(v => this.SelectedVehicleTypes.Contains(v.VehicleTypeId));
return predicate;
}
I then pass that predicate expression to a function that does this:
public IList<VehicleViewData> GetVehiclesByExpression(Expression<Func<VehicleEntity, bool>> whereClause)
{
using (IDataAccessAdapter adapter = this._adapter.Create())
{
LinqMetaData metaData = new LinqMetaData(adapter);
return ToVehicleView(
(metaData.Vehicle
.Where<VehicleEntity>(whereClause)
.WithPath<VehicleEntity>(v => v.Prefetch(e => e.Employee)
.Prefetch(m => m.Make)
.Prefetch(m => m.Model)
.Prefetch(s => s.VehicleStatus)
.Prefetch(l => l.Lessor)
.Prefetch(a => a.AssignedToLocation)
.Prefetch(o => o.OwnedByLocation)) as ILLBLGenProQuery)
.Execute<EntityCollection<VehicleEntity>>());
}
}
There is other stuff going on but I think you get the picture.
Oh, and no, I don't work for LLBLGen, I've just used their product since the stored procedure version and find it to be quite excellent.