This prior post described the code that is generated when you compile a Silverlight application that uses WCF RIA Services and your own plain old CLR objects (POCOs). This post provides some ideas if something goes horribly wrong and the compiler does not generate the appropriate code.
If you build your solution and you don't get the generated code described in this prior post, here are some things to check:
- In your business layer component (where your business classes are defined):
- The class(es) you want to access from Silverlight must be a public.
- The properties that you want to access from Silverlight must be public.
- The Key attribute must be defined on one of the business class public properties.
- In your Silverlight project:
- Ensure you have a WCF RIA Services link.
- Open the Silverlight project properties. The WCF RIA Services link is defined at the bottom of the properties dialog
- In your ASP.NET project (that was created automatically when you created your Silverlight project)
- You must have a reference defined to your business layer component.
- In your Domain Service class:
- The Domain Service class must be defined within the ASP.NET project.
- It must have the EnableClientAccess attribute on the class.
- It must inherit from the DomainService class (or one of its specialized classes such as LinqToEntiesDomainService if you are using Entity Framework)
- If you define a Query method to retrieve data from your business class, the Query method:
- Must be a method and not a property.
- Must return one of the following:
- A single entity (instance of the business object class)
- An IEnumerable<T> where T is the entity
- An IQueryable<T> where T is the entity
- Can have any parameters.
- Can have any name.
- Can (but does not have to) have a Query attribute to define the method as a query.
- If you define an Update method to update data from your business class, the Update method:
- Must be a method.
- Must have no return value.
- Must pass the entity as a parameter.
- Must either:
- Start with "Update", "Change" or "Modify
- Or have an Update attribute
The first time I tried using WCF RIA services, I did not know about the Key attribute, so did not get any generated code.
More recently, I used a converter to convert some working C# code from my Domain Service class into VB and then could not get any generated code from the VB solution. I did not immediately notice that the converter translated IEnumerable<Customer> to just IEnumerable (non-generic). So the compiler did not know which entity to process.
I hope the above checklist will help you quickly find why you are not getting generated code. If you have other things to add to this checklist, I'd love to hear from you. Please add a comment to this post.
Enjoy!
This post provides a high-level description of what happens when you compile a Silverlight application that uses WCF RIA Services and plain old CLR objects (POCOs) and the resulting generated code. (See this prior post for a code example.)
After you define your Silverlight project, enable WCF RIA Services, and add your Domain Service class with appropriate method(s), you have enough to build your solution and view the generated code.
In Solution Explorer, select your Silverlight project and click the Show All Files button in the toolbar. You will then see a Generated_Code folder containing a *.g.cs file. This contains the code generated by Visual Studio and defines the classes detailed below.
Entity class
This class is generated from information in the business layer (your POCO classes on the server) and includes all of the class's properties. It inherits from the DomainServices Entity class (not to be confused with the Entity Framework (EF) Entity class).
The compiler looks at the properties of the class and any attributes on those properties. It then generates a copy of those properties with their associated attributes in the generated code file.
How does it know which business layer class to use for the Entity class? It determines the appropriate class based on the parameters and/or return values on the methods in your Domain Service class.
If you have a query style method, it returns either a single entity or an IEnumerable or IQueryable of the entity. The compiler uses that information to determine which business layer class to use.
If you have an insert, update, or delete method, the parameter defines the entity. The compiler uses that information to determine which business layer class to use.
The compiler attributes the generated class as a DataContract and each property as a DataMember so it can be processed appropriate when the class is used as part of a WCF service. (WCF RIA Services uses WCF "under the covers".)
Domain Context class
This class is generated from information in the Domain Service class that you created. It inherits from DomainContext.
The compiler defines a WCF service contract in this class. It also defines wrapper methods for the methods in your Domain Service class.
When your Silverlight application calls methods in this class, the class makes the WCF service calls to the Domain Service class in the ASP.NET project.
Here it is in picture form:
Enjoy!
The prior post here detailed how to build a Silverlight application using WCF RIA Services and your own plain old CLR objects (POCOs). That application displayed the data in a grid and allowed updates using a set of labels and textboxes. But the order of the fields was alphabetical, which is rarely the order that you want.
So you could spend some time in the XAML, reordering the controls.
OR, you could add a few more attributes to your business object in your business layer and the fields will be ordered as you want.
Use the DisplayAttribute to:
- Define a field name different from the business object property name.
- Define a short name for use in the grid.
- Define a field to use as a watermark.
- Define the order of the field when building the UI.
- Define the resource type if the strings are defined in a resource file.
First, be sure to import the System.ComponentModel.DataAnnotations namespace. Then add the DisplayAttribute to the properties as shown in this example.
In C#:
[Display(Order=1, Description="Customer's last name", ShortName="LName", Name="Name (Last)")]
public string LastName { get; set; }
In VB:
<Display(Order:=1, Description:="Customer's last name", ShortName:="LName", Name:="Name (Last)")>
Public Property LastName() As String
The result looks like this:
Notice the use of the ShortName in the grid column header and of the Name in the field label. But more importantly for this column, notice the order of the fields both in the grid and in the data entry form.
Use this technique to define the field ordering before you drag and drop a grid or data entry form onto your Silverlight page.
Enjoy!
The prior post here detailed how to build a simple Silverlight application that used WCF RIA Services to communicate with your server-side business layer to retrieve data and display it in a Silverlight DataGrid. This post adds to the prior post, providing an update feature.
In keeping with the general theme of the prior post, this post is going to provide the simplest technique for performing an update in a Silverlight application using your own Plain Old CLR Objects (POCOs) that reside on the server.
So first, the prerequisites:
1) Follow the steps in the prior post here.
2) Build the Data Access Layer.
The prior post "mocked" the data access by returning a simple list from the business layer retrieve method. To try out the save, this won't suffice. The code needs real data access.
Use the information in the post defined here to build your data access layer.
3) Change your Customer class to call the Data Access layer.
In the prior post, the Customer class Retrieve method returned a hard-coded set of customers. This method needs to change to use the Data Access layer. In addition, the code needs a Save method to save the customer data.
In C#:
public List<Customer> Retrieve()
{
List<Customer> custList = new List<Customer>();
DataTable dt = Dac.ExecuteDataTable("CustomerRetrieve");
foreach (DataRow dr in dt.Rows)
{
Customer cust = new Customer
{
CustomerId = (int)dr["CustomerId"],
LastName = dr["LastName"].ToString(),
FirstName = dr["FirstName"].ToString(),
EmailAddress = dr["EmailAddress"].ToString()
};
custList.Add(cust);
}
return custList;
}
public bool Save()
{
bool success = true;
// Call the save
int returnId = Dac.ExecuteNonQuery("CustomerUpdate",
new SqlParameter("CustomerId", CustomerId),
new SqlParameter("LastName", LastName),
new SqlParameter("FirstName", FirstName),
new SqlParameter("EmailAddress", EmailAddress));
if (returnId != 0)
this.CustomerId = returnId;
return success;
}
In VB:
Public Function Retrieve() As List(Of Customer)
Dim custList As New List(Of Customer)
Dim dt As DataTable = Dac.ExecuteDataTable("CustomerRetrieve")
For Each dr As DataRow In dt.Rows
Dim cust As New Customer With {
.CustomerId = CType(dr("CustomerId"), Integer),
.LastName = dr("LastName").ToString(),
.FirstName = dr("FirstName").ToString(),
.EmailAddress = dr("EmailAddress").ToString()}
custList.Add(cust)
Next
Return custList
End Function
Public Function Save() As Boolean
Dim success As Boolean = True
' Call the save
Dim returnId As Integer = Dac.ExecuteNonQuery("CustomerUpdate",
New SqlParameter("CustomerId", Me.CustomerId),
New SqlParameter("LastName", Me.LastName),
New SqlParameter("FirstName", Me.FirstName),
New SqlParameter("EmailAddress", Me.EmailAddress))
If returnId <> 0 Then
Me.CustomerId = returnId
End If
Return success
End Function
NOTE: Normally I build a Customer class (singular) with the properties and methods that mange a single customer and a Customers class (plural) that works with a list of those customers. The functionality for both were added to one class to keep this example as simple as possible.
Once the prerequisites are in place, we can get started.
STEP 1: Add an Update method to the Domain Service class
Open the Domain Service class and add an update method.
In C#:
public void UpdateCustomer(Customer updatedCustomer)
{
}
In VB:
Public Sub UpdateCustomer(ByVal updatedCustomer As Customer)
End Sub
A few notes about this UpdateCustomer method:
- It must be a method.
- Must have no return value.
- Must pass the entity as a parameter.
- Must either:
- Start with "Update", "Change" or "Modify
- Or have an Update attribute
WCF RIA Services recognizes the method as an update method because of its name or Update attribute. It recognizes it as an update of a Customer by its parameter. This method is required for an update operation to be valid in the Silverlight application.
This method does not require any code, unless you want to perform some additional operations on an update. However, the method still needs to exist for the DomainDataSource on the page to accept updates.
This method is called when the code calls SubmitChanges, as shown later in this post.
STEP 2: Override the PersistChangeSet method
The PersistChangeSet method is called when the code calls SubmitChanges, after the code calls the Update method. Use this method to call the Save method on your business objects.
In C#:
protected override bool PersistChangeSet()
{
Customer changedCustomer;
bool success = true;
foreach (var item in ChangeSet.ChangeSetEntries)
{
if (item.Entity.GetType() == typeof(Customer))
{
changedCustomer = (Customer)item.Entity;
success = changedCustomer.Save();
if (!success)
break;
}
}
return success;
}
In VB:
Protected Overrides Function PersistChangeSet() As Boolean
Dim changedCustomer As Customer
Dim success As Boolean = True
For Each item In ChangeSet.ChangeSetEntries
If item.Entity.GetType() = GetType(Customer) Then
changedCustomer = DirectCast(item.Entity, Customer)
success = changedCustomer.Save()
If Not success Then
Exit For
End If
End If
Next
Return success
End Function
WCF RIA Services keeps track of any changes to the entities in the application (Customer in this example). You can access the set of changes using the ChangeSet property.
The Customer class in this example has a Save method that saves one specific customer. So the above code loops through the customers in the ChangeSet and calls the Save method on the customer.
This would be more efficient if you had a Customers class (as mentioned in the Note earlier in this post). Your Customers class could have a save method against a list of customers. You could then call Customers.Save to save all of your customer change instead of calling Save on each individual customer.
STEP 3: Build the UI
Again, keeping with the goal of staying as simple as possible, the UI in this example is a simple set of properties.
1) Open the designer for MainPage.xaml by double-clicking on the file.
2) Open the Data Sources window (Data | Show Data Sources).
Visual Studio automatically added the defined domain context class to your data sources. Change the default UI from a data grid to a details view:
3) Drag and drop the Customer data source onto the designer to the right of the grid (created in the prior post) and Visual Studio creates the user interface for you:
4) Add the Save button as shown above.
In the code behind for the Save button, add code as follows.
In C#:
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
if (customerDomainDataSource.HasChanges)
customerDomainDataSource.SubmitChanges();
}
In VB:
Private Sub SaveButton_Click(ByVal sender As System.Object,
ByVal e As System.Windows.RoutedEventArgs) Handles SaveButton.Click
If CustomerDomainDataSource.HasChanges Then
CustomerDomainDataSource.SubmitChanges()
End If
End Sub
This code checks for changes and if the DomainDataSource on the page is changed, it calls SubmitChanges. SubmitChanges first calls the UpdateCustomer method and then the PersistChangeSet method, which in turn calls the Save method of the Customer business object.
STEP 4: Run
If you run the application, you should get this:
This works because when you drag and drop the data source onto the page, Visual Studio defines the labels and textboxes and binds the textboxes to the DomainDataSource that was added in the prior post. The DomainDataSource takes care of managing the updates.
For a more complete example, download the extended source from here.
If you want to learn more about using Silverlight and RIA Services, come and see my talk at VSLive in Redmond Washington August 4, 2010. See this post for more information.
Use these techniques as a starting point when performing update operations using your server-side business layer from a Silverlight application.
Enjoy!
This post demonstrates how to use Silverlight and WCF RIA Services to access YOUR server-side business objects to build a line of business application.
This particular example is as absolutely simple as possible to help you get up and running with these technologies. To meet this goal, this first post covers only how to retrieve data. Later posts will cover update operations.
So first, the prerequisites:
1) Download and install Silverlight 4 and WCF RIA Services 1.0.
Visual Studio 2010 comes with Silverlight 3. You need to download and install the Silverlight 4 Tools for Visual Studio 2010 from here. It comes with WCF RIA Services, so no additional download is required for RIA.
2) Download the Silverlight Toolkit.
This download provides many additional controls and themes. You can download it from here.
3) Build the Business Layer component.
When building a Silverlight/RIA application, you have probably already built your business layer and have business objects that you want to use in your Silverlight application. But if not, you need to do that first.
This example uses an overly simplified business layer component with the Customer class shown below.
Build a standard Class Library project and add the following code:
In C#:
using System.Collections.Generic;
namespace ACM.BLCSharp
{
/// <summary>
/// Manages a customer
/// </summary>
public class Customer
{
public int CustomerId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string EmailAddress { get; set; }
/// <summary>
/// Retrieves a list of customers.
/// </summary>
/// <returns></returns>
/// <remarks>
/// In a "real" application, this code would
/// call a data access component that retrieves
/// the data from a database.
/// </remarks>
public List<Customer> Retrieve()
{
List<Customer> custList = new List<Customer>
{new Customer()
{ CustomerId = 1,
FirstName="Bilbo",
LastName = "Baggins",
EmailAddress = "bb@hob.me"},
new Customer()
{ CustomerId = 2,
FirstName="Frodo",
LastName = "Baggins",
EmailAddress = "fb@hob.me"},
new Customer()
{ CustomerId = 3,
FirstName="Samwise",
LastName = "Gamgee",
EmailAddress = "sg@hob.me"},
new Customer()
{ CustomerId = 4,
FirstName="Rosie",
LastName = "Cotton",
EmailAddress = "rc@hob.me"}};
return custList;
}
}
}
In VB:
''' <summary>
''' Manages a customer
''' </summary>
''' <remarks></remarks>
''' <editHistory></editHistory>
Public Class Customer
Public Property CustomerId As Integer
Public Property FirstName() As String
Public Property LastName() As String
Public Property EmailAddress() As String
''' <summary>
''' Retrieves a list of customers.
''' </summary>
''' <returns></returns>
''' <remarks>
''' In a "real" application, this code would
''' call a data access component that retrieves
''' the data from a database.
''' </remarks>
Public Function Retrieve() As List(Of Customer)
Dim custList As New List(Of Customer) From
{New Customer() With
{.CustomerId = 1,
.FirstName = "Bilbo",
.LastName = "Baggins",
.EmailAddress = "bb@hob.me"},
New Customer() With
{.CustomerId = 2,
.FirstName = "Frodo",
.LastName = "Baggins",
.EmailAddress = "fb@hob.me"},
New Customer() With
{.CustomerId = 3,
.FirstName = "Samwise",
.LastName = "Gamgee",
.EmailAddress = "sg@hob.me"},
New Customer() With
{.CustomerId = 4,
.FirstName = "Rosie",
.LastName = "Cotton",
.EmailAddress = "rc@hob.me"}}
Return custList
End Function
End Class
NOTE: This code does not use a data access layer at this time. To keep this example as simple as possible, the Retrieve method "mocks" a retrieve that would call your data access component and retrieve the data from the database. This code will be replaced in a later post to actually access a database.
NOTE: Normally I build a Customer class (singular) with the properties and methods that mange a singe customer and a Customers class (plural) that works with a list of those customers. The functionality for both were added to one class to keep this example as simple as possible.
Once the prerequisites are in place, we can get started.
STEP 1: Attribute your business objects to work with Silverlight
There are several different attributes you can define in your business objects so they work better with Silverlight, but in keeping with our goal of creating the simplest Silverlight/RIA/POCO example, you are only required to have one.
For WCF RIA services to work properly, you must have the Key attribute defined on the property that represents the unique key for your business object. This is most often the property of the class that stores the primary key value from your underlying table. In this example, it is the CustomerId property.
The Key attribute is defined in System.ComponentModel.DataAnnotations. So you must add a reference to that namespace and import it to use it.
In C#:
using System.ComponentModel.DataAnnotations;
…
[Key()]
public int CustomerId { get; set; }
In VB:
Imports System.ComponentModel.DataAnnotations
…
<Key()>
Public Property CustomerId As Integer
STEP 2: Add the Silverlight Project
1) Right-click on the solution and select Add | New Project.
2) Select Silverlight | Silverlight Application.
Be sure to select the template under Visual Basic for VB applications or the template under Visual C# for C# applications.
3) Check Enable WCF RIA Services.
Be sure to check the WCF RIA Services checkbox as shown below.
When you add a Silverlight project, Visual Studio creates both a Silverlight project and a Web (ASP.NET) project. The ASP.NET project is crucial because:
- It provides the startup code to start up your Silverlight project.
- It provides the WCF service "behind the scenes" to communicate between your Silverlight application and your business objects.
NOTE: If you already have a Silverlight project, you can still enable WCF RIA Services by accessing the Silverlight project properties and defining the WCF RIA Services link as shown below.
STEP 3: Set a reference in the ASP.NET project to your business layer component.
Select the ASP.NET project. Then add a reference to your business layer component.
The solution for this post has both the C# and VB components. If you are working through this example in your language of choice, you should only have one business layer component project on this list.
By setting this reference, the ASP.NET project code can call your business layer.
STEP 4: Add a Domain Service Class
Add a domain service class to the ASP.NET application. This is the class that calls your business layer methods.
The class must be in the ASP.NET application and not the Silverlight application because you cannot define a direct reference between your Silverlight application and a standard class library project. This makes sense because the Silverlight application runs in the client browser and the ASP.NET application runs on the server where it can access your business layer component.
To add a domain service class, right-click on the ASP.NET application and select Add | New Item. Then pick the Domain Service Class template.
This is where this example varies greatly from an Entity Framework example. If you are using Entity Framework, you will get the list of entities here and can set some entity attributes. If you are using your own business objects, you don't have anything shown in the Entities grid. That's fine. Just click OK.
Since you are building your own Domain Service Class, Visual Studio will inherit your Domain Service class from DomainService. If you were building the class using Entity Framework, the class would instead inherit from LinqToEntitiesDomainService.
In most cases, you will build one Domain Service Class for each business class that you want to access from your Silverlight project.
STEP 5: Add a Query method to the Domain Service class
The query method in the Domain Service class will in turn call the Retrieve method in the business layer component.
In C#:
namespace ACM.SilverlightCSharp.Web
{
using System.Collections.Generic;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using ACM.BLCSharp;
[EnableClientAccess()]
public class CustomerDomainService : DomainService
{
public IEnumerable<Customer> GetCustomers()
{
Customer cust = new Customer();
return cust.Retrieve();
}
}
}
In VB:
Imports System.ServiceModel.DomainServices.Hosting
Imports System.ServiceModel.DomainServices.Server
Imports ACM.BLVB
<EnableClientAccess()> _
Public Class CustomerDomainService
Inherits DomainService
Public Function GetCustomers() As IEnumerable(Of Customer)
Dim cust As New Customer
Return cust.Retrieve
End Function
End Class
A few notes about this GetCustomers method:
- It must be a method and not a property.
- Must return one of the following:
- A single entity
- An IEnumerable<T> where T is the entity
- An IQueryable<T> where T is the entity
- Can have any parameters.
- Can have any name.
- Can (but does not have to) have a Query attribute to define the method as a query.
WCF RIA Services recognizes the method as a query method because of its return value. Therefore, the return value must specify the entity either by returning it or returning an IEnumerable or IQueryable of the entity.
If you build at this point and view all files in your Silverlight project, you will see a Generated_Code folder containing a *.g.cs file. This contains the code generated by Visual Studio and defines the following classes:
- Entity class (Customer in this example): This class is generated from the business layer and includes all of the entity's properties.
- Domain Context class (CustomerDomainContext in this example): This class makes the WCF service calls to the Domain Service class in the ASP.NET project.
In addition, the generated code creates a service contract for the ASP.NET Domain Service class.
[For more information on the generated code, see this post.]
Step 6: Build the UI
Again, keeping with the goal of staying as simple as possible, the UI in this example is a data grid.
1) Open the designer for MainPage.xaml by double-clicking on the file.
2) Open the Data Sources window (Data | Show Data Sources).
Visual Studio automatically added the defined domain context class to your data sources:
Simply drag and drop the Customer data source onto the designer and Visual Studio creates the user interface for you:
STEP 7: Run
If you run the application, you should get this:
This works because when you drag and drop the data source onto the page, Visual Studio defines a DomainDataSource control and a DataGrid control on the page. It sets the DomainDataSource QueryName property to "GetCustomersQuery" and sets the DataGrid ItemsSource property to the DomainDataSource. This causes the code to call your GetCustomers method and use the results to populate the grid.
NOTE: The generated code appends the "Query" suffix to your method name in the domain context class (CustomerDomainContext). That is the method that is called from the UI, which in turn calls the GetCustomers method that you created in the domain service class (CustomerDomainService), which in turn calls the Retrieve method in your business layer via a WCF service.
So your Silverlight application is calling your business layer using the ASP.NET application as a WCF service. Here it is in picture form:
To add a feature to update your data, see this post.
For a more complete example, download the extended source from here.
If you want to learn more about using Silverlight and RIA Services, come and see my talk at VSLive in Redmond Washington August 4, 2010. See this post for more information.
Use these techniques as a starting point when accessing your server-side business layer from a Silverlight application.
Enjoy!
In this prior post, I provided information on registering for the VSLive! conference on August 2-6 in Redmond Washington and on the talks I am presenting at that conference. If you would like a "sneak peak" at the source code for my two talks, you can get it all from my company's Web site.
Enjoy!
If you are working on a large project, your Solution Explorer can get unwieldy and difficult to manage. Solution Folders can help.
Solution folders are logical folder groupings of projects or files within a project.
For example, here is a solution with a set of projects:
There are only 11 projects here, which may be small compared to a "real" application, but it will demonstrate the usefulness of solution folders.
To add folders at the solution level to organize your projects:
- Click on the solution in Solution Explorer.
- Select Add | New Solution Folder.
- Give the folder a logical name.
- Drag and drop projects in Solution Explorer into the new folder.
Note: This does not create folders in your file system. It only defines logical folders within your solution.
After Step 2, the folder then appears in Solution Explorer:
In this example, we want to move all of the test projects into a folder, so the solution folder is named "Test Projects". The test projects are then dragged to the new folder. The result is shown below:
When you aren't working with your test projects, you can close the folder, making Solution Explorer a little easier to work with.
To add folders at the project level to organize your project files:
- Click on the project in Solution Explorer.
- Select Add | New Folder.
- Give the folder a logical name.
- Drag and drop project file in Solution Explorer into the new folder.
Note: This does create folders in your file system and moves the dragged files to that folder.
In this example, we want to group the python files into their own folder.
The result is shown below:
Use this technique any time you want to organize your projects or project files into folders.
Enjoy!
EDIT [7/13/10]: Added a second note to make it clear that folders defined at the solution level do *not* create folders in the file system. Folders defined within a project *do* created folders in the file system.
If you like to keep your hands on the keyboard, here is a tip for accessing any Visual Studio window.
Ctrl + Tab
When you are in Visual Studio, just press Ctrl + Tab. You will get a popup window that looks something like this:
Keep holding down the Ctrl key and use the arrow key to move around to the desired window. Release the Ctrl key and the selected window gets focus.
Enjoy!
The Solution Explorer in Visual Studio 2010 still has no search feature (at least not without a Visual Studio Extension). But it does support a type-ahead feature. Though not new, it is a little known feature. So here are the details.
1) Ensure the Solution Explorer has focus.
2) Start typing.
In the screen shot below, I had typed "C".
A few tips:
- It won't search any closed folders.
- If you type a set of letters, it will jump to the first entry that matches and continue to match entries as you type.
- If you type the same letter several times, it will match the next entry that begins with the typed letter.
- Unless of course you have an entry with double-letters, such as "cctest".
Enjoy!
Renaming or moving files in Visual Studio is never an easy task. But one of the lesser known enhancements in Visual Studio 2010 is the ability to change the directory location of a project.
Here is the scenario. The team decides that the projects need to be renamed. To keep things consistent, the directory file locations for those projects are also renamed. In this example, the projects and project folders were all renamed to include a prefix of "ACM."
So now you open the project and get this:
Notice that all of the projects are marked as "unavailable". This is because the solution file contains a path to each of the projects and that path is not longer correct because it was renamed.
In prior versions of Visual Studio, you would then need to manually edit the solution file to fix these issues. But Visual Studio 2010 provides an editable File path property. So you can edit the location of the files directly from within Visual Studio.
Select the project to update. Then view the Properties Window. Click on the … icon in the File path property to edit the project path and/or file name.
NOTE: The File path property is ONLY available for edit if the project is shown as "(unavailable)" in the Solution Explorer. In all other cases, the File path property is read-only.
After you are finished editing the path, right-click on the project in Solution Explorer and select Reload Project.
This is much easier (and less error prone) than hand-editing the project file.
Enjoy!
EDIT: Added a note to make it clear that this is ONLY available if the project is defined as "unavailable". Otherwise the File path property is read-only.
Visual Studio 2010 opens with a new start page:
One of the nice new features on the start page is the ability to pin projects so they will always appear in the Recent Projects list.
If I want to try out some code, I sometimes start up a new "test" project. If I do that too many times, the most recently used file list drops my most important projects. By pinning my important projects, I can ensure that they will always appear in the list.
To access this feature, hover over the project in the Recent Projects list on the left of the start page. The pin icon appears as shown below:
Click on the pin icon to pin the project to the start page. That project will always appear on the Recent Projects list. Click again to un-pin the project.
Enjoy!
There is so much to know these days with all of the new technologies, techniques, and tools in Visual Studio and the .NET framework. How do you even decide which technologies deserve your attention and which you can "give a miss"?
One of the benefits of a technical conference like VSLive! is to figure out which new things are a "must know", which are a "fun to know", which are a "wait to know", and which are a "never need to know".
My presentations are on "Best Kept Secrets in Visual Studio 2010 and .NET 4.0" and "Silverlight, WCF RIA Services, and Your Business Objects".
The first talk covers may of the tips, tricks, and lesser known features of Visual Studio and the .NET Framework. Most of these items should be on your "must know" or "fun to know" list.
The Silverlight talk covers how to communicate between Silverlight and your own business objects or plain old CLR objects (POCOs).
Click here to sign up. Use the promotional code: NSZV18 and you'll receive:
- The all-access Best Value Conference Package for just $1,495, a savings of $300.00 off the standard price of $1,795.
Or
- The 3-Day Conference Package for just $1,195, a savings of $300.00 off the standard price of $1,495.
You must use the priority code NSZV18 to receive the discount.
Hope to see you there!
This prior post demonstrated a very simple technique for writing to an XML file that has a set of elements. This post extends that example showing how to write an XML file that has both elements and attributes.
Here is the XML file created by this example:
<?xml version="1.0" encoding="utf-8"?>
<Employee>
<LastName>Baggins</LastName>
<FirstName>Bilbo</FirstName>
<PhoneNumber PhoneType="Work">(925)555-1234</PhoneNumber>
</Employee>
This XML file has an Employee root node and LastName, FirstName, and PhoneNumber elements. The PhoneNumber element has a PhoneType attribute.
There are several ways to write this file in C# or VB as shown below.
In C# (adding individual nodes):
var doc = new XDocument();
var emp = new XElement("Employee");
emp.Add(new XElement("LastName", textBox1.Text));
emp.Add(new XElement("FirstName", textBox2.Text));
var phone = new XElement("PhoneNumber", textBox3.Text);
phone.Add(new XAttribute("PhoneType", comboBox1.Text));
emp.Add(phone);
doc.Add(emp);
doc.Save("Employee.xml");
This code creates an XML document and adds an Employee root node. It then creates the three elements.
Since the Add method does not return a value, the code needs to create the phone element separately. This provides a reference to that element that can be used to add the attribute.
The Employee element is then added to the XML document and saved.
In C# (using functional construction):
var emp = new XElement("Employee",
new XElement("LastName", textBox1.Text),
new XElement("FirstName", textBox2.Text),
new XElement("PhoneNumber",
new XAttribute("PhoneType", comboBox1.Text),
textBox3.Text));
emp.Save("Employee.xml");
Functional construction allows you to create an XML string in a single statement leveraging the XElement and XAttribute constructors. See this msdn link for more information on functional construction.
This example also demonstrates how you can build an XML file without explicitly creating an XDocument object as was done in the prior example.
In C# (using the Parse method):
var emp = XElement.Parse(@"<Employee>
<LastName>" + textBox1.Text + @"</LastName>
<FirstName>" + textBox2.Text + @"</FirstName>
<PhoneNumber PhoneType='" + comboBox1.Text + "'>" + textBox3.Text + @"</PhoneNumber>
</Employee>");
emp.Save("Employee.xml");
This example builds the XML using strings. The XML elements, attributes, and values are built up as a single string. Then the Parse method is used to parse the string into an XElement.
The biggest downside of this approach is that concatenating strings in this way can be prone to error. It is best used when you have a pre-defined single string that you want to parse into an XML Element. For example:
XElement newElement = XElement.Parse(@"<Area name='Wauwatosa'/>");
[From the post found here.]
In VB (adding individual nodes):
Dim doc = New XDocument()
Dim emp = New XElement("Employee")
emp.Add(New XElement("LastName", TextBox1.Text))
emp.Add(New XElement("FirstName", TextBox2.Text))
Dim phone = New XElement("PhoneNumber", TextBox3.Text)
phone.Add(New XAttribute("PhoneType", ComboBox1.Text))
emp.Add(phone)
doc.Add(emp)
doc.Save("Employee.xml")
This code creates an XML document and adds an Employee root node. It then creates the three elements.
Since the Add method does not return a value, the code needs to create the phone element separately. This provides a reference to that element that can be used to add the attribute.
The Employee element is then added to the XML document and saved.
In VB (using functional construction):
Dim emp = New XElement("Employee",
New XElement("LastName", TextBox1.Text),
New XElement("FirstName", TextBox2.Text),
New XElement("PhoneNumber",
New XAttribute("PhoneType", ComboBox1.Text),
TextBox3.Text))
emp.Save("Employee.xml")
Functional construction allows you to create an XML string in a single statement leveraging the XElement and XAttribute constructors. See this msdn link for more information on functional construction.
This example also demonstrates how you can build an XML file without explicitly creating an XDocument object as was done in the prior example.
In VB (using XML literals):
Dim emp = <Employee>
<LastName><%= TextBox1.Text %></LastName>
<FirstName><%= TextBox2.Text %></FirstName>
<PhoneNumber PhoneType=<%= ComboBox1.Text %>>
<%= TextBox3.Text %></PhoneNumber>
</Employee>
emp.Save("Employee.xml")
This example uses XML literals to perform the same operation. It builds the XML string using the <%= %> replacement syntax to populate the value of each element and attribute.
Use any of the above techniques when you need to write values to an XML file with elements and optionally with attributes.
Enjoy!
This prior post detailed how to write to an XML file. This post details how to read that XML file.
The example reads the values and populates three TextBoxes, but you can use this technique to read any information from an XML file.
XML File:
<?xml version="1.0" encoding="utf-8"?>
<Employee>
<LastName>Baggins</LastName>
<FirstName>Bilbo</FirstName>
<PhoneNumber>(925)555-1234</PhoneNumber>
</Employee>
In C#:
var doc = XDocument.Load("Employee.xml");
var emp = doc.Descendants("Employee").FirstOrDefault();
textBox1.Text = emp.Element("LastName").Value;
textBox2.Text = emp.Element("FirstName").Value;
textBox3.Text = emp.Element("PhoneNumber").Value;
In VB:
Dim doc = XDocument.Load("Employee.xml")
Dim emp = doc.Descendants("Employee").FirstOrDefault()
TextBox1.Text = emp.Element("LastName").Value
TextBox2.Text = emp.Element("FirstName").Value
TextBox3.Text = emp.Element("PhoneNumber").Value
In VB using XML Literals:
Dim doc = XDocument.Load("Employee.xml")
TextBox1.Text = doc...<LastName>.Value
TextBox2.Text = doc...<FirstName>.Value
TextBox3.Text = doc...<PhoneNumber>.Value
This code first loads the XML file into an XML document.
In the first two examples, the code finds the first Employee node. In this case, there is only one. It then uses the Element function to retrieve the defined element ("LastName", "FirstName", and "PhoneNumber") from the Employee node. The Value property provides the string value of the element. This value is assigned to the TextBox.
The third example above uses XML literals to retrieve the elements from the XML document. The "..." syntax denotes to search any descendants, so the Employee node does not have to be found first.
Use one of the above techniques any time you need to read values from an XML file.
Enjoy!
Sometimes, you just need to write some values to an XML file. If you are targeting the .NET Framework 3.5 or higher, you can use XDocument and XElement to do this easily.
This post demonstrates how to write values to an XML string. The example writes the values from three TextBoxes, but you can use this technique to write any information to an XML file.
In C#:
var doc = new XDocument();
var emp = new XElement("Employee");
emp.Add(new XElement("LastName", textBox1.Text));
emp.Add(new XElement("FirstName", textBox2.Text));
emp.Add(new XElement("PhoneNumber", textBox3.Text));
doc.Add(emp);
doc.Save("Employee.xml");
In VB:
Dim doc = New XDocument()
Dim emp = New XElement("Employee")
emp.Add(New XElement("LastName", TextBox1.Text))
emp.Add(New XElement("FirstName", TextBox2.Text))
emp.Add(New XElement("PhoneNumber", TextBox3.Text))
doc.Add(emp)
doc.Save("Employee.xml")
In VB using XML Literals:
Dim doc = New XDocument()
Dim emp = <Employee>
<LastName><%= TextBox1.Text %></LastName>
<FirstName><%= TextBox2.Text %></FirstName>
<PhoneNumber><%= TextBox3.Text %></PhoneNumber>
</Employee>
doc.Add(emp)
doc.Save("Employee.xml")
This code first creates a new XDocument that defines the XML document.
In the first two examples, the code then creates a new XElement defining the root node of the XML string. In this case, the root node is "Employee", but it can be anything.
The sub elements are then added to the Employee root element. In this case, the sub elements are "LastName", "FirstName", and "PhoneNumber" and the values of these elements come from the TextBoxes.
The third example above uses XML literals to perform the same operation. It builds the XML string using the <%= %> replacement syntax to populate the value of each element.
In each example, the new Employee element is then added to the XML document and saved.
The resulting XML file is shown below.
XML File:
<?xml version="1.0" encoding="utf-8"?>
<Employee>
<LastName>Baggins</LastName>
<FirstName>Bilbo</FirstName>
<PhoneNumber>(925)555-1234</PhoneNumber>
</Employee>
(See this post for information on reading the above XML file.)
Use one of the above techniques any time you need to write values to an XML file.
Enjoy!
EDIT 6/7/10: To write XML documents that include attributes, see this other post.
The trick to obtaining the list of database names from SQL Server is to know the name of the system stored procedure that you need to call.
This post presents a static/shared method for obtaining the names of the databases in a particular SQL Server instance.
In C#:
public static List<string> GetDatabaseNames()
{
string connString = null;
List<string> databaseNames = new List<string>();
// Be sure to replace this with your connection string.
connString = "Data Source=.\sqlexpress;Integrated Security=True";
if (!string.IsNullOrWhiteSpace(connString))
{
using (SqlConnection cn = new SqlConnection(connString))
{
// Open the connection
cn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = cn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_databases";
using (SqlDataReader myReader = cmd.ExecuteReader())
{
while ((myReader.Read()))
{
databaseNames.Add(myReader.GetString(0));
}
}
}
}
}
return databaseNames;
}
In VB:
Public Shared Function GetDatabaseNames() As List(Of String)
Dim connString As String
Dim databaseNames As New List(Of String)
' Be sure to replace this with your connection string.
connString = "Data Source=.\sqlexpress;Integrated Security=True"
If Not String.IsNullOrWhiteSpace(connString) Then
Using cn As SqlConnection = New SqlConnection(connString)
' Open the connection
cn.Open()
Using cmd As SqlCommand = New SqlCommand()
cmd.Connection = cn
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "sp_databases"
Using myReader As SqlDataReader = cmd.ExecuteReader()
While (myReader.Read())
databaseNames.Add(myReader.GetString(0))
End While
End Using
End Using
End Using
End If
Return databaseNames
End Function
This code begins by defining a new generic List of strings. This list defines the return value for the function.
The connection string is hard-coded to an instance of SQL Server express with Windows authentication. To make this code more generalized, you could pass in the connection string or read it from a configuration file. It was hard-coded here just to keep the example simple.
The routine then defines and opens the connection and defines a command. The CommandType is stored procedure and the stored procedure name is "sp_databases". This is a system stored procedure and should exist in your SQL Server.
The routine executes the command returning a DataReader with the results of the stored procedure. Each database name is obtained using the DataReader and added to the list.
Once you have the list of names in a generic List, you can do just about anything with them. For example, you can bind them to a ComboBox.
In C#:
DatabaseComboBox.DataSource = databaseNames;
In VB:
DatabaseComboBox.DataSource = databaseNames
The result is shown below:
Use this technique any time you need to obtain the list of database names from SQL Server.
Enjoy!
There are some scenarios that require building a connection string at runtime. For example, if you are building a database utility or if your application allows working with multiple databases. In these cases, you may want to ask the user for the basic parts of the connection and not require entry of a syntactically correct connection string.
Here is an example of a Windows Forms dialog for entry of connection information:
(Thanks to my friend Robin for creating this nice user interface.)
After the user makes the appropriate selections, you can use the entered values to create a connection string.
The xxxConnectionStringBuilder classes in System.Data can help you with this. Use the SqlConnectionStringBuilder class if you are accessing SQL Server or the OdbcConnectionStringBuilder class if you are referencing a Microsoft Access or other Odbc database.
This example uses the SqlConnectionStringBuilder. But you can readily replace it with OdbcConnectionStringBuilder when necessary.
The following example defines a shared/static function that builds a connection string from the controls on the form shown above.
Be sure to set a reference to System.Data.SqlClient.
In C#:
private static string CreateConnectionString()
{
SqlConnectionStringBuilder sqlBuilder =
new SqlConnectionStringBuilder();
if (!string.IsNullOrWhiteSpace(ServerNameTextBox.Text)) {
sqlBuilder.DataSource = ServerNameTextBox.Text;
if (!string.IsNullOrWhiteSpace(DatabaseNameComboBox.Text) {
sqlBuilder.InitialCatalog = DatabaseNameComboBox.Text;
}
sqlBuilder.IntegratedSecurity =
AuthenticationComboBox.SelectedValue ==
AuthenticationType.Windows ? true : false;
// For SQL Server authentication, need a user Id and password
if (sqlBuilder.IntegratedSecurity == false) {
sqlBuilder.UserID = UserIdTextBox.Text;
sqlBuilder.Password = PasswordTextBox.Text;
}
}
return sqlBuilder.ConnectionString;
}
In VB:
Private Shared Function CreateConnectionString() As String
Dim sqlBuilder As New SqlConnectionStringBuilder
If Not String.IsNullOrWhiteSpace(ServerNameTextBox.Text) Then
sqlBuilder.DataSource = ServerNameTextBox.Text
If Not String.IsNullOrWhiteSpace(DatabaseNameComboBox.Text) Then
sqlBuilder.InitialCatalog = DatabaseNameComboBox.Text
End If
sqlBuilder.IntegratedSecurity = If
(AuthenticationComboBox.SelectedValue =
AuthenticationType.Windows, True, False)
' For SQL Server authentication, need a user Id and password
If sqlBuilder.IntegratedSecurity = False Then
sqlBuilder.UserID = UserIdTextBox.Text
sqlBuilder.Password = PasswordTextBox.Text
End If
End If
Return sqlBuilder.ConnectionString
End Function
NOTE: The above code uses the implicit line continuation feature that is new in VB 10 (VS 2010). If you have an older version of VB, you will need to add the line continuation character ( _ ) where necessary.
The CreateConnectionString function shown above creates a new instance of the SqlConnectionStringBuilder. It then assigns the properties of the SqlConnectionStringBuilder from the data entry fields. The ConnectionString property then contains the appropriate connection string.
You can then use the returned connection string to connect to the selected database.
Enjoy!
With the new multi-monitor support, an underused Visual Studio menu option just became much more useful. You can use the Window | New Window option to view a second code window with the same code file. The result looks like this:
You can then drag one of the code windows so they are side by side or drag one to your other monitor. This allows you to view two different parts of your code file at the same time.
HOWEVER, the Window | New Window option is disabled when working with Visual Basic code files. The feature was put in, but not fully tested. So it was disabled.
If you really want to use this feature in VB.NET, you can remove the registry entry that disables it.
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Languages\Language Services\Basic]
"Single Code Window Only"=dword:00000001
USE AT YOUR OWN RISK!
I submitted a Connect Issue to have this put in by default. You can vote for it here:
https://connect.microsoft.com/VisualStudio/feedback/details/559243/enable-the-windows-new-window-feature-for-visual-basic
Enjoy!
We are lucky to have two of Redmond's finest down here in Silicon Valley for a full day .NET 4.0 and VS 2010 event on May 15, 2010. Click here for more information or to register.
I am happy to be providing one of the talks at the event. I am giving the "What's New in VB 10" talk. VB and C# continue to get closer together in terms of feature set, so that means more feature parity.
With parity in mind, VB 10 has the following new features:
My talk covers these items and plenty more. If you want the sample code for the talk, you can find it here.
Enjoy!
If you are doing any type of statistical analysis, you probably need to calculate mean, median and mode. There are lots of places on the Web you can find the calculations. This post is different than most in that it uses LINQ and Lambda expressions.
Mean is the statistical average of a set of numbers. This one is easy with LINQ because of the Average function.
In C#:
int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };
double mean = numbers.Average();
Debug.WriteLine(("Mean: " + mean));
In VB:
Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}
Dim mean As Double = numbers.Average()
Debug.WriteLine("Mean: " & mean)
The result is:
Mean: 2.88888888888889
This code uses the Average extension method on the IEnumerable class to calculate the mean, or average, of the numbers.
Median is the middle number of a set of numbers. If there is an even number of entries, it is the average of the two middle numbers.
In C#:
int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };
int numberCount = numbers.Count();
int halfIndex = numbers.Count()/2;
var sortedNumbers = numbers.OrderBy(n=>n);
double median;
if ((numberCount % 2) == 0)
{
median = ((sortedNumbers.ElementAt(halfIndex) +
sortedNumbers.ElementAt((halfIndex - 1)))/ 2);
} else {
median = sortedNumbers.ElementAt(halfIndex);
}
Debug.WriteLine(("Median is: " + median));
In VB:
Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}
Dim numberCount As Integer = numbers.Count
Dim halfIndex As Integer = numbers.Count \ 2
Dim sortedNumbers = numbers.OrderBy(Function(n) n)
Dim median As Double
If (numberCount Mod 2 = 0) Then
median = (sortedNumbers.ElementAt(halfIndex) +
sortedNumbers.ElementAt(halfIndex - 1)) / 2
Else
median = sortedNumbers.ElementAt(halfIndex)
End If
Debug.WriteLine("Median is: " & median)
The result is:
Median is: 3
This code first counts the numbers and divides the count by 2 to find the middle of the list. Note that the VB code uses the backslash (\) to perform an integer division where the C# code uses a forward slash (/) for the division.
It then sorts the numbers in order using the OrderBy extension method and a Lambda expression that simply orders by the numbers.
The last step is to get the element at the middle (if odd) or the average of the two middle elements (if even). The result is the median.
Mode is the number that occurs the largest number of times.
In C#:
int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };
var mode = numbers.GroupBy(n=> n).
OrderByDescending(g=> g.Count()).
Select(g => g.Key).FirstOrDefault();
Debug.WriteLine(("Mode is: " + mode));
In VB:
Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}
Dim mode = numbers.GroupBy(Function(n) n).
OrderByDescending(Function(g) g.Count).
Select(Function(g) g.Key).FirstOrDefault
Debug.WriteLine("Mode is: " & mode)
The result is:
Mode is: 4
This code uses the GroupBy extension method on IEnumerable to group the numbers by number. It then orders them by the count and selects the first one. This provides the number that occurs the most times.
Use these techniques whenever you need to calculate the mean, median, or mode.
Enjoy!
More Posts
Next page »