I wanted to let you all in on a little secret. When I am feeling the urge to write a linq post and can’t find something write about I go to Microsoft’s 101 LINQ samples. For the most part they’re simple examples of a lot of the features that LINQ has to offer.
The list is not all-inclusive but it usually provides some starting point for a LINQ post. The samples are pretty simple and there’s not a lot of explanation. I try to take some of the samples and expand and explain them. Also I run all samples through visual studio. I have had some of the samples there not compile so try them out before you blindly trust them.
Next time you see a LINQ post here don’t be suprised if it started life as a 101 LINQ sample. I just take a simple example and then beat as much blood from it as I can.
Brian
November 12th, 2008 in
C# |
No Comments
I’m about to start working on a project that includes some potential .NET Framework 4.0 stuff. Dug around and found the links for downloading the ctp for Visual Studio 2010 and .NET Framework 4.0 CTP.
I haven’t downloaded it yet since we haven’t officially started the project but I’m eager to get things going
.
November 6th, 2008 in
C# |
No Comments
I have done zero real advertising about this site other then Facebook, setting up google sitemaps and mentioning it to friends. No pingbacks, trackbacks or putting up my url anywhere really. Some how, however, I managed to get 185 unique hits for the month of October. That is without my hits from the three locations I would go to it from and without any bots. That’s right, 185 people went to my website and they weren’t me!
Here is a rundown of the top 10 most popular pages. These are not unique hits but represent the total number of hits per page.
So how did people even find the site? Well, google really. Here is a partial list of google terms that led to this site:
| 61 different keyphrases |
Number to site from Search |
| get mouse position to screen in wpf |
6 |
| hibernate upsert |
4 |
| formatexception |
3 |
| format exception |
3 |
| wpf contentproperty |
3 |
| cannot implicitly convert type system.linq.iorderedenumerable |
2 |
| contentproperty wpf |
2 |
| postgres insertorupdate |
2 |
| c# sql server upsert |
2 |
| upsert postgresql |
2 |
| Other phrases |
53 |
Anyways, I’d like to have one significant post a week and one trivial post a week. As I saw with October that’s not an easy task but I think I’m up for the challenge. I don’t profess to be an expert on anything. Anywhere I might sound like an expert I try to link to a real expert. I’m just trying to pass on the knowledge I gain in the form of ramblings on developing in the Windows(tm MS i’m sure) world.
My next step is to try and generate some contriversey. I think my posts so far have been too “tutorial” in nature. I’ll try to keep those going since I have fun writing them but I think some topics intented to spawn debate need to be next. So any recommendations from those of you who stop by on a regular basis would be nice. I have allowed anonymous comments so tell me how you really feel. Of course I reserve the right to delete and edit posts as necessary but don’t let that stop you
Thanks to all of you that stop by and don’t be afraid to say, “Hi!” (and yes, i mean literally 
Brian
November 3rd, 2008 in
Uncategorized |
1 Comment
Had some trouble with using a converter when databinding on a GridView yesterday so I thought I’d do a quick write up.
Imagine for one instance you have the following class:
public class EntityDataItem
{
public string Name { get; set; }
public string Type { get; set; }
public DateTime CreatedTS { get; set; }
}
Now imagine you have the following xaml:
<ListView Name="EntitiesGrid">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Type}"/>
<GridViewColumn Header="CreatedTS" DisplayMemberBinding="{Binding CreatedTS}"/>
</GridView>
</ListView.View>
</ListView>
To bind to the grid all you would have to do on the window load event is:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<EntityDataItem> items = new List<EntityDataItem>();
items.Add(new EntityDataItem { Name = "A", Type = "Person", CreatedTS = DateTime.Now });
items.Add(new EntityDataItem { Name = "B", Type = "Equipment", CreatedTS = DateTime.Now });
items.Add(new EntityDataItem { Name = "C", Type = "Location", CreatedTS = DateTime.Now });
items.Add(new EntityDataItem { Name = "D", Type = "Unit", CreatedTS = DateTime.Now });
EntitiesGrid.ItemsSource = items;
}
Well now, that’s all fine and good. But now imagine you were, oh, I don’t know, working on some military project that wanted you do use a specific format for the date. The way it is now when the binding happens it will use the default ToString() which isn’t acceptable in this case.
So we need to use a converter. Converters let you go from any object to any object on databinding. Let’s start by creating the converter:
[ValueConversion(typeof(DateTime), typeof(String))]
public class DateToMilitaryConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return "";
DateTime date = (DateTime)value;
return date.ToString("dd MMM yyyy HHmm", DateTimeFormatInfo.InvariantInfo);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string strValue = value.ToString();
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))
{
return resultDateTime;
}
return value;
}
}
then in the resources for your xaml define:
<local:DateToMilitaryConverter x:Key="dateConverter"/>
assuming the local namespace is defined as your local assembly or whatever namespace you used for the converter.
Here’s where things get a bit tricky. You can’t put a converter directly on a GridViewColumn. Where you can put it is on the Binding. To do that make your xaml look like:
<ListView Name="EntitiesGrid">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Type}"/>
<GridViewColumn Header="CreatedTS">
<GridViewColumn.DisplayMemberBinding>
<Binding Path="CreatedTS" Converter="{StaticResource dateConverter}"/>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
You can see here we are tapping into the DisplayMemberBinding propery of the GridViewColumn and assigning a Binding to it. On the Binding we can use a converter. As I mentioned though you can go from any object to any other object. Remember, however, regardless of what you go to, the ToString() will be called on it. A couple of points of interest on the converter. The source of the data in the real binding, not my contrived example, is a DateTime? which means the value could be null. Thus I have accounted for that in the converter. Additionally I allow for a class cast exception rather then doing:
DateTime date = value as DateTime;
if (value == null)
return "";
in case someone else were to use my class where it doesn’t belong. Basically the converter handles DateTime? but nothing else, as it should be.
Well, I meant this one to be short and sweet and it was.
Later,
Brian
UPDATE:
It has been brought to my attention that you can do converters inline with the DisplayMemberBinding.
<GridViewColumn
Header="CreatedTS"
DisplayMemberBinding="{Binding Path=CreatedTS, Converter={StaticResource dateConverter}}"
/>
Live and learn 
Brian
Interesting video here.
http://release.theplatform.com/content.select?pid=x7aVOMrlfkkijQwcLllwk6WjB5JE0zrF
I haven’t fact checked everything in this (since it’s obviously not mine) so use your own head in accepting the facts as they are portrayed. It’s still interesting.
Brian
This post is kind of not LINQ related, kind of LINQ related.
I’ve mentioned lambdas before but wanted to move more into what is more at the core of lambdas. That is more along the lines of delegates.
We all know about delegates, don’t we?
“No Brian, we don’t.”
“Ivan is that you?” (Yes, I’ve named my inner voice Ivan, IV, inner voice, Ivan, get it?)
“Yes”
“Um, is that all?”
“What else do you want me to say? I want to know about delegates”
Ok, well, then, delegates are basically pointers to methods.
Let me give you an example:
public partial class Window1 : Window
{
delegate int del(int i);
public Window1()
{
del myDelegate = x => x * x;
int square = myDelegate(5);
}
}
You can see here that I have defined my type for the delegate below the class declaration
delegate int del(int i);
The delegate, myDelegate, takes an int and returns an int. Then inline with anycode I can define a method that uses these parameters.
Now I could do something like:
del myDelegate2 = x => --x;
int decrement = myDelegate2(4);
but without having to declare a delegate type let’s take a look at Func so we can create functions inline without having to declare a delegate type first. We technically are declaring a type because of generics and we are limited to the small set of parameters that Func allows us, but that’s neither here nor there since I would assume, for an inline query this should be able to handle your needs. If you can’t do it in what Func provides I suspect you will probably just do a standard method.
Func types are:
Func(TResult) Delegate
Func(T, TResult) Delegate
Func(T1, T2, TResult) Delegate
Func(T1, T2, T3, TResult) Delegate
Func(T1, T2, T3, T4, TResult) Delegate
So let’s take a look at some code:
int[] list1 = { 0, 1, 2, 3, 4, 5 };
Func<int, bool> lookForEven = x => x % 2 == 0;
var myListOfEvens = list1.Where(lookForEven);
The Where method in the int list takes Func<int, bool> predicate. (It will take Func<int, int, bool> predicate as well). So I created the method lookForEven with a lambda, x => x % 2 == 0, and then I can use it in the Where method. Now, the operations of the method don’t have to be defined inline. They can be a method that matches the types of the declaration.
private bool BadlyNamedMethod(int val)
{
return Math.Pow(val, 2) < 20;
}
private void TestMyMethod()
{
int[] list1 = { 0, 1, 2, 3, 4, 5 };
var sqrsLessThenTwenty = list1.Where(BadlyNamedMethod);
}
Now let’s look at taking multiple inputs:
int[] list1 = { 0, 1, 2, 3, 4, 5 };
int[] list2 = { 0, 2, 4, 6, 8 };
Func<int, int, bool> myFunc = (x,y) => x == y;
foreach (var x in list1)
{
foreach (var y in list2)
{
if (myFunc(x, y))
{
MessageBox.Show("Found Match");
break;
}
}
}
Basically I’m creating an intersect.
“Brian, there is an intersect function already.”
“Yes Ivan, I know”
“Fine, f-off”
Yes, yes, yes, as Ivan pointed out I could just as easily done:
var myListOfEquals = list1.Intersect(list2);
but then you wouldn’t see me using the Func.
Basically, I’m creating a function that takes two parameters, x and y, and testing for equality. The system knows what I’m going to do becuase when I created “myFunc” I defined that it would take int, int and return bool.
But we’re not limited to primitives.
private void TestMyMethod()
{
Person[] people = {new Person {FirstName = "Brian", LastName = "Mullen", Id = 1},
new Person {FirstName = "George", LastName = "Smith", Id = 2},
new Person {FirstName = "Mabel", LastName = "Jones", Id = 3}};
Func<Person, string> compareByName = p => p.LastName;
var orderedByLastName = people.OrderBy(compareByName);
}
Want to compare people by a list of strings?
Func<Person, string, int?> personCompare = (x, y) =>
{
if (x.FirstName == y)
return x.Id;
return null;
};
Anyways, have to go. But hopefully this is a nice introductory look to delegates, Func and some lambdas.
Brian
October 17th, 2008 in
C# | tags:
delegates,
Func,
LINQ |
No Comments
Hibernate used an InsertOrUpdate (aka Upsert) method to save values (netTiers uses Save for the same thing). This made things really simple, no need to work out if you have inserted the object, just call InsertOrUpdate and Bob’s your uncle. Here I present the InsertOrUpdate method (well, copied from http://blogs.msdn.com/miah/archive/2008/02/17/sql-if-exists-update-else-insert.aspx but I made it universally applicable
. A handy little thing that lets you use do both.
(MS SQL)
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
IF @@ROWCOUNT=0
INSERT INTO Table1 VALUES (...)
It’s nice, it’s simple and does what needs to be done without a lot of work.
Here is the same approach can be used for postgresql, oracle and mysql.
(PostgreSQL)
UPDATE Table1 SET (…) WHERE Column1=’SomeValue’
IF NOT FOUND
INSERT INTO Table1 VALUES (…)
(Oracle)
UPDATE Table1 SET (…) WHERE Column1=’SomeValue’
IF sql%rowcount=0
INSERT INTO Table1 VALUES (…)
(MySQL)
INSERT INTO Table1 (Column1, …) VALUES (SomeValue, …)
ON DUPLICATE KEY UPDATE …
See also:
http://blog.cutterscrossing.com/index.cfm/2008/2/4/SQL-Tricks-Whats-an-Upsert
UPDATE: Btw, if you’re using SQL Server 2008 you can use MERGE to do this.
Java folks, please don’t get scared off by the C# examples, the contents of the post apply regardless if it is java or C#.
None of us use Asserts enough. It’s that simple, we don’t. Asserts are essentially a way to ensure we don’t get a value we shouldn’t have. The great thing about using asserts is that they aren’t included in final code (compiled in “Release”), only code complied with “Debug”. They make testing easier by verifying that when code is being tested in Debug that it doesn’t go beyond some condition. They can also be used to ensure when troubleshooting that you have the values you should.
Take a look at the code below:
private void pass_Click(object sender, RoutedEventArgs e)
{
int temp = 1;
Debug.Assert(temp > 0);
MessageBox.Show(“Pass”);
}
private void fail_Click(object sender, RoutedEventArgs e)
{
int temp = -1;
Debug.Assert(temp > 0);
MessageBox.Show(“Fail”);
}
This is a sample of using an assert. I know the example is trivial but I wanted to show a simple use of it. These are simply a Click event on a “Pass” button and a Click event on a “Fail” button. I would recommend you create your own solution and put these on a pass and fail button so you can see and understand what happens when Debug.Assert is called.
Now lately I’ve been coming across a lot of code like (anonymous-ized for your protection):
else
{
MessageBox.Show(“ERROR: YOU SHOULD NEVER SEE THIS MESSAGE. Invalid TransformString!”, “SERIOUS ERROR occurred in MyClass:MyMethod”, MessageBoxButton.OK, MessageBoxImage.Error);
}
I’m sorry but this is simply wrong.
Code Complete 2nd edition has a great chapter on Assertions. The above example is exactly where asserts should be used.
Per the guidelines for “Using Assertions” (McConnnel, Code Complete 2, 2004) the following guidelines should apply for using assertions:
“Use error-handling code for conditions you expect to occur; use assertions for conditions that should never occur.
Avoid putting executable code into assertions.
Use assertions to document and verify preconditions and post conditions.
For highly robust code, assert and then handle the error anyway.”
That’s right. The very first usage guideline says to use asserts for conditions that should never occur.
Take a look at a sample from Code Complete (originally VB but I ported it to C#):
private double Velocity(double Latitude, double Longitude, double Elevation)
{
//Verify preconditions
Debug.Assert(Latitude >= -90 && Latitude <= 90, “Invalid Latitude”);
Debug.Assert(Longitude >= 0 && Longitude <= 360, “Invalid Longitude”);
Debug.Assert(Elevation >= -500 && Elevation <= 75000, “Invalid Elevation”);
double velocity = 0;
/* DO THE WORK */ //Verify postconditions
Debug.Assert(velocity >= 0 && velocity <= 600,
“Invalid Velocity”,
“Can’t seem to properly work the velocity formula”);
return velocity;
}
On this McConnell has to say:
“If the variables Latitude, Longitude, and Elevation were coming from an external source, invalid values should be checked and handled by error-handling code rather then by assertions. If the variables are coming from a trusted, internal source, however, and the routine’s design is based on the assumption that these values will be withn their valid ranges, then assertions are appropriate.” (McConnell, 2004)
Now assertions aren’t the end-all, be-all of validating values. Chances are that in most cases you are still going to want to validate and do error checking. But they’re a good start.
And just for the java folks that hung around, an assert in java:
//Check to make sure we’re not going to do a divide by zero
assert denominator != 0: “denominator is 0″;
Refs:
Code Complete (Second Edition), Microsoft Press, Steve McConnell, 2004
C# Online.NET – Assert
Debug.Assert Method
And just for the java folks:
Programming With Assertions
September 29th, 2008 in
C#,
Programming Standards | tags:
Assert |
1 Comment
I ‘ve been trying to come up with things that lead to a series of follows up stuff. This way I don’t have to think too hard about coming up with subjects
and it makes writing them easier since I don’t have to spend so much time in general on them. And now from:
http://www.ayende.com/Blog/archive/2008/08/27/What-I-am-working-on.aspx
Oren is his name and he is one of the bloggers I follow pretty close. He’s been doing some pretty incredible stuff with a product he helped write called “Rhino Mocks” that allows you to create mock objects for testing and to set test conditions on those objects (ok, there’s a lot more but that scratches the surface) and in general he’s a pretty incredible coder.
Well, recently he posted some code that seems essentially how not to develop software with so many bad coding standards it would keep me busy for awhile. Take a look at the below code:
public class TaxCalculator
{
private string conStr;
private DataSet rates;
public TaxCalculator(string conStr)
{
this.conStr = conStr;
using (SqlConnection con = new SqlConnection(conStr))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM tblTxRtes", con))
{
rates = new DataSet();
new SqlDataAdapter(cmd).Fill(rates);
Log.Write("Read " + rates.Tables[0].Rows.Count +
" rates from database");
if (rates.Tables[0].Rows.Count == 0)
{
MailMessage msg = new MailMessage("important@legacy.org", "joe@legacy.com");
msg.Subject = "NO RATES IN DATABASE!!!!!";
msg.Priority = MailPriority.High;
new SmtpClient("mail.legacy.com", 9089).Send(msg);
Log.Write("No rates for taxes found in " + conStr);
throw new ApplicationException("No rates, Joe forgot to load the rates AGAIN!");
}
}
}
}
public bool Process(XmlDocument transaction)
{
try
{
Hashtable tx2tot = new Hashtable();
foreach (XmlNode o in transaction.FirstChild.ChildNodes)
{
restart:
if (o.Attributes["type"].Value == "2")
{
Log.Write("Type two transaction processing");
decimal total = decimal.Parse(o.Attributes["tot"].Value);
XmlAttribute attribute = transaction.CreateAttribute("tax");
decimal r = -1;
foreach (DataRow dataRow in rates.Tables[0].Rows)
{
if ((string)dataRow[2] == o.SelectSingleNode("//cust-details/state").Value)
{
r = decimal.Parse(dataRow[2].ToString());
}
}
Log.Write("Rate calculated and is: " + r);
o.Attributes.Append(attribute);
if (r == -1)
{
MailMessage msg = new MailMessage("important@legacy.org", "joe@legacy.com");
msg.Subject = "NO RATES FOR " + o.SelectSingleNode("//cust-details/state").Value + " TRANSACTION !!!!ABORTED!!!!";
msg.Priority = MailPriority.High;
new SmtpClient("mail.legacy.com", 9089).Send(msg);
Log.Write("No rate for transaction in tranasction state");
throw new ApplicationException("No rates, Joe forgot to load the rates AGAIN!");
}
tx2tot.Add(o.Attributes["id"], total * r);
attribute.Value = (total * r).ToString();
}
else if (o.Attributes["type"].Value == "1")
{
//2006-05-02 just need to do the calc
decimal total = 0;
foreach (XmlNode i in o.ChildNodes)
{
total += ProductPriceByNode(i);
}
try
{
// 2007-02-19 not so simple, TX has different rule
if (o.SelectSingleNode("//cust-details/state").Value == "TX")
{
total *= (decimal)1.02;
}
}
catch (NullReferenceException)
{
XmlElement element = transaction.CreateElement("state");
element.Value = "NJ";
o.SelectSingleNode("//cust-details").AppendChild(element);
}
XmlAttribute attribute = transaction.CreateAttribute("tax");
decimal r = -1;
foreach (DataRow dataRow in rates.Tables[0].Rows)
{
if ((string)dataRow[2] == o.SelectSingleNode("//cust-details/state").Value)
{
r = decimal.Parse(dataRow[2].ToString());
}
}
if (r == -1)
{
MailMessage msg = new MailMessage("important@legacy.org", "joe@legacy.com");
msg.Subject = "NO RATES FOR " + o.SelectSingleNode("//cust-details/state").Value + " TRANSACTION !!!!ABORTED!!!!";
msg.Priority = MailPriority.High;
new SmtpClient("mail.legacy.com", 9089).Send(msg);
throw new ApplicationException("No rates, Joe forgot to load the rates AGAIN!");
}
attribute.Value = (total * r).ToString();
tx2tot.Add(o.Attributes["id"], total * r);
o.Attributes.Append(attribute);
}
else if (o.Attributes["type"].Value == "@")
{
o.Attributes["type"].Value = "2";
goto restart;
// 2007-04-30 some bastard from northwind made a mistake and they have 3 months release cycle, so we have to
// fix this because they won't until sep-07
}
else
{
throw new Exception("UNKNOWN TX TYPE");
}
}
SqlConnection con2 = new SqlConnection(conStr);
SqlCommand cmd2 = new SqlCommand();
cmd2.Connection = con2;
con2.Open();
foreach (DictionaryEntry d in tx2tot)
{
cmd2.CommandText = "usp_TrackTxNew";
cmd2.Parameters.Add("cid", transaction.SelectSingleNode("//cust-details/@id").Value);
cmd2.Parameters.Add("tx", d.Key);
cmd2.Parameters.Add("tot", d.Value);
cmd2.ExecuteNonQuery();
}
con2.Close();
}
catch (Exception e)
{
if (e.Message == "UNKNOWN TX TYPE")
{
return false;
}
throw e;
}
return true;
}
private decimal ProductPriceByNode(XmlNode item)
{
using (SqlConnection con = new SqlConnection(conStr))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM tblProducts WHERE pid=" + item.Attributes["id"], con))
{
DataSet set = new DataSet();
new SqlDataAdapter(cmd).Fill(set);
return (decimal)set.Tables[0].Rows[0][4];
}
}
}
}
If this code doesn’t make you shudder I worry about you and suspect you shouldn’t be working as a computer programmer.
Over the course of the next few weeks, along with my posts on LINQ I want to spend some time analyzing what is wrong with this and help to try to define some of the established coding standards with .Net as well as coding in general.
Let’s start with the obvious. We all know I am anti-goto, with good reason. There is really no place in any modern high-level language for gotos. Look at this:
else if (o.Attributes["type"].Value == “@“)
{
o.Attributes["type"].Value = “2“;
goto restart;
// 2007-04-30 some bastard from northwind made a mistake and they have 3 months release cycle, so we have to
// fix this because they won’t until sep-07
}
Okay, if you look at the flow of the foreach this is in this else/if and breaks the flow of the code sending the execution back up to the top in mid-loop restarting everything over again.
But Brian, this had to be done, don’t you see the comments?
No my young padawan, it doesn’t. I’m not saying the fix for this shouldn’t be in place, what I’m saying is that the goto shouldn’t be there. If the original programmer thought about this a bit more he would have seen that if he simply put the statement at the beginning of the foreach as an if statement like:
if (o.Attributes["type"].Value == “@“)
{
o.Attributes["type"].Value = “2“;
// 2007-04-30 some bastard from northwind made a mistake and they have 3 months release cycle, so we have to
// fix this because they won’t until sep-07
}
it would have had the same effect without needing the goto.
But Brian, by putting it later in the code then most of the time you will be able to try and capture the ifs earlier and then you won’t have to do a compare on every row for “@”.
So what? How do you know that every row won’t have the “@” for the value? In that case you actually hurt your code execution by not putting the if at the beginning of the foreach loop. Plain and simple gotos hurt code (see my earlier post on gotos with references on such). The studies have been done and there are no reason for them in a modern language.
On to the next point of the code:
MailMessage msg = new MailMessage("important@legacy.org", "joe@legacy.com");
Well, what’s wrong with this?
What if Joe quits the company? What if Important quits the company? These values should not be embedded in the code.
But Brian, Important is a not person you silly guy! It’s a mailing group.
Wow, sarcasm is not lost on you.
In modern .Net applications you have the Settings. This allows you to store values in an easy to use xml file. To get the values back out you don’t have to even do any special parsing, you just do (assuming you named your settings “MySettings”):
MailMessage msg = new MailMessage(MySettings.WhoToSendEmailTo);
That’s right. When you add settings to your application you can then simply refer to the values in them directly as a property of the static Settings object in your project. Then, in the future if Joe or Important should no longer get the emails then you simply modify the xml file in the application directory (or use the Visual Studio settings editor) and change who it should go to. No need to recompile. It’s that simple. To add a settings, right click on the project name, select “Add New” and click on Settings file. Name it as you like and Visual Studio will open a settings editor where you can add these properties and values.
Brian, you’re a dumb ass. I tried this. My application spans multiple projects and the settings file is only scoped at the project level.
Fine, if you want to be that way be that way. Don’t use the settings features built in to the framework. But whatever you do don’t embed this stuff into the application code. If your smart and have been using a data access layer (DAL) like nhibernate or netTiers then getting and storing these values in the database should be trivial. While up front it’s a bit more of a pain, on the back end it is easier because then for each instance of your application running you don’t have to modify the settings file and deploy to each application. You just get the values out of the database.
Well, that’s it for now. Hopefully you all might be interested in this and if you aren’t you’ll still be getting the code. Doing a series like this is easy for me and allows me more time to do real work so there
Next time we’ll look at DALs and refactoring.
Later ‘yall,
Brian
September 19th, 2008 in
C# | tags:
C#,
coding standards |
No Comments
LINQ allows you do define simple functions/methods in-line with the query you are doing. These are called lambda expressions.
They have a special format to allow for in-line expressions.
Take a look at:
c => c + 1
Basically this is:
for each value c give me c + 1
Rather then complicating things with full-blown LINQ queries lets cheat and just use simple arrays. For example:
int[] myInts = { 1, 9, 2, 8, 3, 7, 4, 6, 5 };
var largeInts = myInts.Where(x => x > 5);
The where clause requires a lambda expression that returns a boolean. Here we’re saying:
for each value x give me values where x > 5
Um, Brian, I have graduated college, this is kindergarten stuff.
Fine, lets go a bit more complicated:
string[] digits = { “zero”, “one”, “two”, “three”, “four”, “five”, “six”, “seven”, “eight”, “nine” };var shortDigits = digits.Where((digit, index) => digit.Length < index);
That’s two, yes, two variables here. If you haven’t realized by now LINQ is really syntactic sugar. When using arrays with LINQ, since all it is really going to do is just iterate over the values for you, you have access to an index variable that represents the index of where the query is.
Here it is saying
for each digit and its corresponding index give me the values where the length of the digit string is less then the index
meaning shortDigits ends up having the values:
{ “five”, “six”, “seven”, “eight”, “nine” }
Now, before we go too far I need to take a step back and clarify something from the last post. Remember I gave a basic LINQ statement as:
var sortedFiles = from file in files orderby file.LastWriteTime descending select file;
and then went on to say:
from file in files
basically for each file in our files list
orderby file.LastWriteTime
LastWriteTime is a property on FileInfo. files is a list of FileInfo, therefore each file is a FileInfo object. Intelli-sense is fully supported here so when you type in file and put the dot is shows you all the properties on file as if it were a standardly declared FileInfo.
descending
Like a standard DESC to an orderby clause. Like sql it implicitly orders by ASC so if you want descending you have to add it.
select file
return the file that applies to the orderby clause. This may seem a bit redundant and I would agree but it is still needed.
Okay, now that you have read this for a second time I really want to address that last section. It is only redundant when you want the full object available in the list. If all I wanted was a list of file names I would do:
var sortedFiles = from file in files orderby file.LastWriteTime descending select file.Name;
Yes, you can return a single property from the object.
BUT WAIT, THERE’S MORE!
Yes, Ron Popeil here with the amazing Create-The-Object-On-The-Fly ability of LINQ and the 3.5 framework.
That’s right, Set It and Forget it! and it will generate types ON THE FLY!!!
Say I not only want the file name but also want the time when the file was created (CreationTime). Well, just add that to your LINQ and now you have an IEnumerable with two properties:
var sortedFiles = from file in files orderby file.LastWriteTime descending select new { file.Name, file.CreationTime };
Yes, here you can see the real power of the var. sortedFiles is now a list consisting of some object (really a System.Linq.Enumerable.SelectIterator<System.IO.FileInfo,<string,System.DateTime>> object) that has the properties Name and CreationTime. This ability is called Anonymous Types.
Taking this idea one step further, remember that a LINQ query is really just SQL you can put in your code to enumerate over lists.
That’s it Brian, your f-in with me!
Yes, yes I am.
Wow, deja vu.
As you have seen the ability to create anonymous types on the fly as well as using lambda expressions aren’t really just simply enumerating over a list. But back to the sql idea. Let’s do a join in a LINQ query.
Say we have a Product class that looks like:
public class Product
{
public int ProductId;
public string Name;
}
and an Inventory class that looks like:
public class Inventory
{
public int InventoryId;
public int ProductId;
public int NumberOnHand;
}
now take a look at this:
List<Inventory> InventoryList = new List<Inventory>();
InventoryList.Add(new Inventory { ProductId = 1, NumberOnHand = 3 });
InventoryList.Add(new Inventory { ProductId = 2, NumberOnHand = 0 });
List<Product> ProductList = new List<Product>();
ProductList.Add(new Product { ProductId = 1, Name = “Apple” });
ProductList.Add(new Product { ProductId = 2, Name = “Orange” });
var ProductsInInventory = from InventoryItem in InventoryList
join ProductItem in ProductList
on InventoryItem.ProductId equals ProductItem.ProductId
where InventoryItem.NumberOnHand > 0
select new { ProductItem.Name, InventoryItem.NumberOnHand };
But Brian, that’s not how it’s done in SQL.
Well, really inner voice? Like I’ve never done SQL before.
Wasn’t the medication supposed to stop you from hearing me? ‘Cause it doesn’t seem to be doing a great job.
Oh that? Stopped takin’ it. Figured without my inner voice I was just talking to myself.
But yes, Inner Voice is correct, that is not how it’s done. But I did say that LINQ was like sql, not exactly sql.
The way a join is done is a bit different then in SQL when doing an implicit inner join but it is still pretty simple and actually very similar to doing an explicit inner join. We just get so used to doing implicit joins we forget about explicit joins. Once again, things are done in LINQ to facilitate the compiler optimizations and Intelli-sense.
Well, now your foot is wet and I’ve taken you a bit further into the world of LINQ. Next on the topic is more on lambda expressions and other types of joins.
Later ‘yall,
Brian (and his inner voice)
September 17th, 2008 in
C# | tags:
C#,
LINQ |
No Comments