Archive for the ‘VB.NET’ Category.
December 3, 2011, 10:29 pm
The C++ const keyword is a powerful tool. It basically allows you to pass a pointer or reference that can only be used to call const methods. ie: You can implement a mutable class and then pass an immutable reference to that class (the keyword also does other things, but that’s not what I’m writing about). This defines a much stronger contract. When a function asks for a pointer to an instance of Foobar it can promise that it will not change that object. For example:
#include <string>
#include <iostream>
// Here's a piece of a class with one const method and one non-const method
class Person
{
public:
std::string getName() const { return name; }
void setName(std::string newName) { name = newName; }
private:
std::string name;
};
// This function asks for a references to a person but promises not to change the object
void doSomethingWithPerson(const Person &person)
{
std::cout << person.getName(); // <-- this is fine
person.setName("hello"); // <-- COMPILE ERROR
}
int main(int argc, char *argv[])
{
Person myPerson;
myPerson.setName("Foobar");
doSomethingWithPerson(myPerson);
return EXIT_SUCCESS;
}
I love code like this. When I call doSomethingWithPerson() I know for sure that it will not change the object (assuming I have properly labelled my const methods). This is an area where I generally think of languages like Java or C# as being superior to C++. We commonly assume that these higher level languages are better at protecting programmers from themselves (or other programmers) but here it is not the case. You can achieve a similar result by creating an immutable class, but what if you do want to change it in other cases? Maybe you want to pass an immutable instance to one function and a mutable instance to another. This can surely be implemented, but it likely involves creating a second class (maybe a read-only subclass) which is kind of a pain. In reality developers often do not bother to create read-only versions of classes where it is warranted, but they will take the time to put const where it is needed.
After a lot of time just working in .NET I have returned to doing some C++ programming on a hobby project. Suddenly I feel dirty when I go back to .NET and start passing objects as function parameters willy nilly without using a const reference. It really is amazing how programming in different languages can change the way you think.
October 26, 2011, 6:48 pm
In recent memory I have twice tried to write reflection code that seemed totally badass at the time but turned out to be just plain bad. One case involved the use of GetCallingAssembly() and the other involved navigating the stack trace with stackTrace.GetFrame(1). While there is nothing inherently wrong with these functions (ie: they can be used perfectly fine for logging or debugging) you absolutely cannot depend on a specific result due to runtime optimizations by the .NET JIT compiler. For example, imagine A calls B and B calls C. If C runs GetCallingAssembly() then you would expect to get the assembly of B; however, if function B gets inlined in A (which is not uncommon) then technically A is directly calling C, and you will actually get A as a result! In fact, if you check the MSDN documentation they even warn you about this:
If the method that calls the GetCallingAssembly method is expanded inline by the just-in-time (JIT) compiler, or if its caller is expanded inline, the assembly that is returned by GetCallingAssembly may differ unexpectedly.
The documentation for GetExecutingAssembly() has no such warning, but just to be safe I prefer to be explicit with something like this: myObject.GetType().Assembly
February 6, 2011, 5:31 pm
I often find that I get so used to using List<T> for my collections that I forget to consider whether it is really the right tool for the job. If the collection is not ordered, and you don’t want duplicates then there are a lot of benefits to using HashSet<T>. For example with List<T> you’ll find yourself writing code like this:
if (!myCollection.Contains(newString))
{
myCollection.Add(newString);
}
This is a little verbose and it’s also inefficient. Every time you call Contains() the entire collection has to be scanned. With a HashSet<T> set operations are optimized and the code is simpler:
myHashSet.Add(newString);
The other benefit is that you actually enforce the no duplicate rule since adding the same element more than once will have no effect. Here’s what the MSDN documentation says about HashSet<T>:
The HashSet(Of T) class provides high-performance set operations. A set is a collection that contains no duplicate elements, and whose elements are in no particular order.
In practice I am finding that there are actually a lot of scenarios when I really want a set and not a list. So the next time you create a collection, think about whether you can use a HashSet.
May 19, 2010, 7:15 pm
The title of this article is a pretty obvious statement, but it’s actually pretty easy to forget and it can lead to some painful bugs. Here’s a code snippet whose output may seem surprising:
Module Module1
Sub Main()
Dim query =
From name In {"one", "two", "three"}
Select New User(name)
Dim x = query.First
Dim y = query.First
Console.WriteLine(x Is y)
Console.ReadKey()
End Sub
End Module
Public Class User
Public Sub New(ByVal username As String)
Console.WriteLine("Creating user: " & username)
End Sub
End Class
At first glance it looks like x and y are the same object, but since query is just a query, not an actual collection, the result will be fetched independently each time you call First. When the code is run, the console output looks like this:
Creating user: one
Creating user: one
False
This shows that the constructor was called twice for the same string, which explains why x and y are actually different objects. In a case like this it is better to put ToList at the end of the query:
Dim users =
(From name In {"one", "two", "three"}
Select New User(name)).ToList()
Bugs like this can be particularly problematic when you pass around an object of type IEnumerable and the programmer assumes that they are dealing with a collection, when they are really dealing with a query. So… watch out!
April 2, 2010, 11:14 am
Up until Visual Studio 2010, simple property definitions were always ridiculously verbose. For example:
Private _title As String
Public Property Title() As String
Get
Return _title
End Get
Set(ByVal value As String)
_title = value
End Set
End Property
That’s 9 lines of code just to make a simple string property. Luckily, in VB 10 we will be able to write these simple properties in one line:
Public Property Title As String
This is great, but what if things get a little more complicated? Obviously this syntax just uses the default getter and setter, both of which have the same scope. When I write my properties I like avoid using ReadOnly and instead make the setter private so that I can still encapsulate the setting code within the class. With VB 10 you still have to define the property the old way in order to do this:
Private _title As String
Public Property Title() As String
Get
Return _title
End Get
Private Set(ByVal value As String)
_title = value
End Set
End Property
But now it’s back to 9 lines just because I wanted the setter to be private. In C#, programmers have the luxury of writing properties like this:
public string Title { get; private set; }
Furthermore, neither language allows you to use a default getter and custom setter (or vice versa) probably because it would make it difficult for them both to use the same backing store, but that’s a whole other tangent. Overall, the new abbreviated syntax for properties in VB 10 is great for the simple cases, but you have to revert to the old, verbose method if you want to deviate from the default in even the slightest way.
March 6, 2010, 9:17 am
A common pattern in database design is to use make a column required, give it a default value and then never think about it when doing INSERTs. A perfect example would be a createdDate column on the Users with a default value of GetDate(). Here’s the full table definition:
- userID (identity key)
- userName
- password
- ts (timestamp)
- createdDate (default value = GetDate())
In this case we can easily insert into the table without worrying about the createdDate, userID, or ts columns:
INSERT INTO Users (userName, password) VALUES ('asdf', 'qwer')
However, since this is the 21st century, we don’t want to do this in SQL, we want to do it with an ORM. Unfortunately, LINQ to SQL doesn’t do a very good job with this.
Using context = New TestDataContext
' Output SQL to the console for debugging
context.Log = Console.Out
' Attach a new user and submit the changes
Dim newUser As New User With {.userName = "NewUser", .password = "password"}
context.Users.InsertOnSubmit(newUser)
context.SubmitChanges()
End Using
The above code generates the following INSERT statement when SubmitChanges() is called (note: I replaced @p0, @p1, etc with their actual values to make the query more readable):
INSERT INTO [dbo].[Users]([userName], [password], [createdDate]) VALUES ('NewUser', 'password', NULL)
This query fails and we get a SqlTypeException because createdDate is NOT NULL and NULL cannot be converted to a valid date. Notice that the generated SQL does not attempt to explicitly set a value for userID or ts. It appears that LINQ to SQL knows how to deal with IDENTITY fields and TIMESTAMPs, but not how to deal with other required columns that happen to have a default value.
I would have expected LINQ to SQL to generate a query that does not explicitly set createdDate so that SQL Server could handle it, but no such luck. You can easily set the createdDate manually like this:
Dim newUser As New User With {.userName = "NewUser", .password = "password", .createdDate = Date.Now}
It really sucks to have to do this every time though, especially if you have many fields to fill in. A possible alternative is to put a partial class on either your DataContext or just on the User class and write some code that will automatically initialize fields like createdDate. If you want to make generic behaviour for this (eg: automatically set columns named “createdDate” to Date.Now when SubmitChanges is called) you can do something like this in the DataContext partial class:
Public Overrides Sub SubmitChanges(ByVal failureMode As ConflictMode)
' NOTE: this is just a sample to get you started
For Each insert In GetChangeSet().Inserts
Dim createdDateProp = insert.GetType.GetProperty("createdDate")
If createdDateProp IsNot Nothing Then
createdDateProp.SetValue(insert, Date.Now, Nothing)
End If
Next
MyBase.SubmitChanges(failureMode)
End Sub
February 9, 2010, 9:23 pm
The great thing about fetching data via a LINQ to SQL query is that you get a nice formatted result and you can easily save back any changes you make with SubmitChanges(). Unfortunately, we all inevitably fall into scenarios where we have to make use of stored procedures for performance or other reasons. If you have a stored procedure whose result set contains columns from just a single table then you can easily map the stored procedure to that table, but in most cases the result set involves multiple tables making things a little more tricky. It’s easy to execute a stored procedure from LINQ to SQL (just drag the SP from the server explorer into the designer and then execute it like a function on the data context) but you lose some of the benefits of LINQ to SQL. First of all, you just get a flat result set instead of a hierarchical result set using the auto generated entity classes. Second, you can’t just make changes to the result and call SubmitChanges. Luckily, with a little extra work, the flat, detached result set can be converted into a hierarchical, attached result set where changes can easily be saved.
If you don’t want to bother reading the whole article and all of the code, here’s the short answer: use the Attach() method.
Below is an example that runs a stored procedure to return all users in the database joined with their articles. The results are converted into an attached list of users, each containing a collection of articles. Notice that not all of the columns need to be known, just the primary key and timestamp are required. For more info on the timestamp, check out this article.
Module Module1
Sub Main()
Using testContext As New TestDataContext
' Print SQL queries to the console for testing purposes
testContext.Log = Console.Out
' Get attached entities
Dim users = GetAttachedUsersWithGroups(testContext)
' Make some random changes to prove the concept
users.First.userName = "foo"
users.First.Articles.First.text = "bar"
' Submit the changes to see what SQL gets executed
testContext.SubmitChanges()
End Using
Console.ReadKey()
End Sub
Public Function GetAttachedUsersWithGroups(ByVal context As TestDataContext) As IEnumerable(Of User)
' Get some data from a stored procedure
Dim result = context.GetAllUsersWithArticles
' Convert flat result set to groups of articles by user
Dim userGroups = From row In result _
Group row By row.userID, row.userTimestamp _
Into articles = Group _
Select userID, userTimestamp, articles
Dim users As New List(Of User)
' Create LINQ to SQL entities
For Each userGroup In userGroups
Dim user As New User With {.userID = userGroup.userID, _
.ts = userGroup.userTimestamp}
For Each article In userGroup.articles
user.Articles.Add(New Article With {.articleID = article.articleID, _
.title = article.title, _
.ts = article.articleTimestamp})
Next
users.Add(user)
Next
' Attach the users to the data context. This will also attach the articles
' because they have been added to each user's Articles collection.
context.Users.AttachAll(users)
Return users
End Function
End Module
February 5, 2010, 7:26 pm
In LINQ to SQL you can chain multiple where clauses like this:
Module Module1
Sub Main()
Using context As New TestDataContext
context.Log = Console.Out
Dim articles = context.Articles.Where(Function(a) a.articleID > 10) _
.Where(Function(a) a.articleID Mod 2 = 0) _
.ToList()
End Using
Console.ReadKey()
End Sub
End Module
This will generate SQL that looks roughly like this:
SELECT ... FROM Articles WHERE articleID > 10 AND articleID % 2 = 0
Since chained where clauses are equivalent to ANDing multiple expressions in a single WHERE, the above SQL is exactly what you would expect to see. Unfortunately, things get more complicated when one of the expressions cannot be converted to SQL, like in this case:
Module Module1
Sub Main()
Using context As New TestDataContext
context.Log = Console.Out
Dim articles = context.Articles.Where(AddressOf FilterArticle).ToList()
End Using
Console.ReadKey()
End Sub
Function FilterArticle(ByVal a As Article) As Boolean
Return a.articleID Mod 2 = 0
End Function
End Module
The above code generates SQL that looks like this:
The query has no where clause, it just loads all the articles and then filters them on the client side. It’s usually optimal to do the filtering on the SQL side, but the behaviour is reasonable. I wouldn’t expect the ORM to be capable of magically converting the contents of the FilterArticle function into SQL (it sure would be nice though). This is still expected behaviour, but here’s an example where things get weird:
Module Module1
Sub Main()
Using context As New TestDataContext
context.Log = Console.Out
Dim articles = context.Articles.Where(AddressOf FilterArticle) _
.Where(Function(a) a.articleID > 10) _
.ToList()
End Using
Console.ReadKey()
End Sub
Function FilterArticle(ByVal a As Article) As Boolean
Return a.articleID Mod 2 = 0
End Function
End Module
This code generates the same SQL as last time:
It is filtering both where clauses on the client side even though the second one could have been converted to SQL. If you flip the where clauses like this:
Module Module1
Sub Main()
Using context As New TestDataContext
context.Log = Console.Out
Dim articles = context.Articles.Where(Function(a) a.articleID > 10) _
.Where(AddressOf FilterArticle) _
.ToList()
End Using
Console.ReadKey()
End Sub
Function FilterArticle(ByVal a As Article) As Boolean
Return a.articleID Mod 2 = 0
End Function
End Module
then you will still get the expected SQL:
SELECT ... FROM Articles WHERE articleID > 10
The where clause that can be converted to SQL is filtered in the SELECT statement, but the clause that cannot be converted is filtered on the client side. I would have hoped that the order of the where clauses would not matter since they are just being ANDed, but that is not the case.
The lesson is that if you need to chain a where clause that cannot be converted to SQL, try to put it at the end of the chain. This can be a real issue if you are using a data access layer that automatically filters queries (eg: for security) with a function that cannot convert to SQL. If all of your LINQ to SQL queries have this built in filter then none of them will ever generate WHERE clauses in the SQL, it will just load the entire table every time.
February 4, 2010, 8:57 pm
Download the code here: WpfLoadingOverlay.zip
A common issue in MDI or TDI style user interfaces is that it can take a long time to render new forms when they are opened. Even a one or two second delay is enough to make an application seem very unresponsive. If there’s nothing you can do to improve the actual performance (ie: the total time to load the form) you can at least improve the perceived performance. This code sample creates a simple form with a tab control and a button. Every time you click the button it adds a new tab with 4000 text boxes. Depending on the speed of your computer the form will probably take around five seconds to load. As soon as you click the button, the new tab appears with a loading animation that continues until the contents have been rendered. This is not a trivial task because we want to render a loading animation at the same time that we are waiting for another long rendering operation to complete. Basically, we need two rendering threads. You can’t have multiple rendering threads in a single window, but you can put your loading animation in a new window with its own rendering thread and make it look like it’s not a separate window.
The loading overlay is a separate, chromeless window that does not appear on the taskbar and disappears as soon as loading is complete. The window is also semi-transparent and automatically positioned exactly over top of the form that is loading, so it looks like it is a part of the existing window.
Download the sample project and try it out.
January 31, 2010, 8:45 pm
If you attach an entity with a required association that is nulled out, you will be unable to call GetChangeSet(). In my opinion, the expected behaviour is that the entity should show up in the change set as though it is valid, but an exception should be thrown when you attempt to call SubmitChanges() because a foreign key constraint has been violated. In fact, with code like this we will get exactly that result (an exception is thrown on SubmitChanges()):
Using testData As New TestDataContext
Dim newArticle As New Article With {.title = "Foobar", _
.text = "blah blah blah"}
testData.Articles.InsertOnSubmit(newArticle)
Dim changes = testData.GetChangeSet()
testData.SubmitChanges()
End Using
There is a required association to the Users table that has not been set at all. Using the following snippet, with the User property explicitly set to Nothing an exception will be thrown on GetChangeSet() instead of SubmitChanges():
Using testData As New TestDataContext
Dim newArticle As New Article With {.title = "Foobar", _
.text = "blah blah blah", _
.User = Nothing}
testData.Articles.InsertOnSubmit(newArticle)
Dim changes = testData.GetChangeSet()
testData.SubmitChanges()
End Using
It gives this error on GetChangeSet():
An attempt was made to remove a relationship between a User and a Article. However, one of the relationship’s foreign keys (Article.userID) cannot be set to null.
It appears that the internal implementation of LINQ to SQL distinguishes between an unset relationship, and one that has specifically been set to Nothing. The awkward thing here is that it is not always easy to avoid this issue since you don’t even have to call InsertOnSubmit. Attaching an entity by setting an association to an already attached object gives the same result.
Using testData As New TestDataContext
Dim existingUser = testData.Users.First
Dim newUserGroup As New UserGroup With {.User = existingUser, .Group = Nothing}
Dim changes = testData.GetChangeSet()
testData.SubmitChanges()
End Using
In this snippet there are two required associations: User and Group. As soon as User is set, the UserGroup entity is attached to the DataContext. However, since Group is Nothing the ChangeSet is now corrupt.
This bug is described in this forum thread where a Microsoft employee recommended that he post it on Connect (Microsoft’s bug tracking site). The bug report on Connect can be found here.