Technorati Tags:
Unit Test,
LINQ,
XML In Part 1, we talked about creating LINQ objects and getting data. This time, we are going to show how to use XML with LINQ to test a SQL database. This part is really not difficult, we are just going to cover the basics of getting started. I have made the conscious decision that I am going to use a SQL database during my development process. You may decide that you want to do use a different methodology. These are some of your options:
Step 1: Generate Data
First, you want to create some test data. Since, I did not really wanted to do this by hand I found a site www.generatedata.com, that has an online utility for generating data. The best part was the different formats that it supports.
Step 2: Create Unit Test Project
Now that I have data, I created a Unit Test project and now I am ready to really get some work done. In this project, I created a "Data" directory and put my generated XML from Step 1. To the right is an example of what my Unit Testing file structure currently looks like.
Step 3: Create Helper
The next thing we want to do is create a helper that will be used to populate XML data to SQL. Next, lets create a class, and name it CustomerHelper. In my class, I made the following adjustments.
- References
- Business Library where my model LINQ objects are held.
- System.Linq
- System.Xml.Linq
- Make the class static. (See below sample)
Next, we need to add some constants to hold the XML and resulting collections that will get loaded. Mine looks like the following:
public static class CustomerHelper
{
static XDocument xmlCustomers;
static List<Customer> customers;
public static List<Customer> Customers
{
get { return customers; }
}
}
Next we need to automatically load the data in the constructor.
xmlCustomers = XDocument.Load("Customers.xml");
customers = (from c in xmlCustomers.Descendants("Customer")
select new Customer
{
FirstName = (string)c.Element("FirstName"),
LastName = (string)c.Element("LastName"),
MiddleName = string.Empty,
Apartment = string.Empty,
Address = (string)c.Element("Address"),
City = (string)c.Element("City"),
Country = "USA",
Email = (string)c.Element("Email"),
PostalCode = (string)c.Element("PostalCode"),
State = (string)c.Element("State"),
Telephone = (string)c.Element("Telephone"),
}
).ToList();
So some explaining, need to be done about what is happening here. First we are loading the XML file into an XDocument. That XDocument will then act just like SQL where we can query against it like any other data. LINQ looks at the object just like it would for SQL except there are some additional properties and methods. An example of the data is below. We query the data and turn it into LINQ SQL objects. The query is doing the following:
- Query the xmlCustomer object looking for the "Customer" nodes.
- In each customer node, we want to take the selected elements and populate them into our Customer object.
- After building the query, execute the code now and return a list of Customers.
<Customers>
<Customer>
<FirstName>Bevis</FirstName>
<LastName>Dalton</LastName>
<Address>5394 Dis Ave</Address>
<City>Tamuning</City>
<State>Wisconsin</State>
<PostalCode>04338</PostalCode>
<Telephone>(772) 462-2385</Telephone>
<Email>lorem.luctus.ut@elitEtiam.ca</Email>
</Customer>
</Customers>
If we try and run this now, it will not work because we need to do a little bit of plumbing first.
Step 4: Start Testing
After creating my unit test file, I add a reference to my LINQ Database Context, which in my case is called InventoryDBDataContext.
InventoryDBDataContext db;
In my Test Initialize I create an instance of my database context,
so each test will have a new one.
[TestInitialize()]
public void MyTestInitialize()
{
db = InventoryDBDataContext.Create();
}
Now I just want to do a simple test to make sure that I can get
data from XML and into SQL.
[TestMethod]
[DeploymentItem(@"\Data\")]
public void ShouldCreateAGroupOfCustomers()
{
ShouldDeleteAllCustomers();
foreach (Customer cust in CustomerHelper.Customers)
{
db.Customers.InsertOnSubmit(cust);
db.SubmitChanges();
}
Assert.IsTrue(db.Customers.Count() >= 5);
}
public void ShouldDeleteAllCustomers()
{
foreach (Customer c in db.Customers)
{
db.Customers.DeleteOnSubmit(c);
}
db.SubmitChanges();
Assert.IsTrue(db.Customers.Count() == 0);
db.ExecuteCommand("DBCC CHECKIDENT ('Customer', RESEED, 0)");
}
The key to this whole process is the [DeploymentItem(@"\Data\"]
attribute. This takes the XML files and makes sure that they
are deployed with the rest of the files. The other key is
that I want to clean up my SQL database after each test, so
I clear out my data and make sure that the Identity field
gets reset so it matches the other XML data which will
be used later.
So, now we are done. You should have working test, that can
take XML data and put it into SQL for Data Populate, but
you also have a set of data that you can then run test against
and test that you got correct results.
In the next installment, we will examine the full completed LINQ
middle tier of our application.
Have fun.
I am currently writing a service using WCF. One of the things that I need to expose is a container that wraps my results to provide counts. I came up with the below implementations. One of the things that becomes a problem is the resulting serialization of the container name. By default, the serialized object would be ContainerOf[T]. By defining a name with a String.Format type of syntax, [DataContract(Name="{0}sContainer")]. Would create the following for the Apple object: ApplesContainer.
Enjoy
/// <summary>
/// Generic Container for holding results
/// </summary>
/// <typeparam name="T"></typeparam>
[DataContract(Name="{0}sContainer")]
public class Container<T>
{
/// <summary>
/// TotalResults that can be returned.
/// </summary>
[DataMember]
public int TotalResults
{
get;
set;
}
/// <summary>
/// Container results.
/// </summary>
[DataMember]
public IEnumerable<T> Results
{
get;
set;
}
}
References:
So, I am going to talk about LINQ. There are a lot of great resources out there on LINQ. I am going to have a series of articles on building an application utilizing LINQ, WCF and other technologies. Some of the things that I want to show is how you can use LINQ as part of your testing. So lets get started.
So the first thing that we need is some data to define. The sample application that we are going to build is a simple home inventory system. The image to the right defines a Customer.
There is three ways that we can generate this data definition.
- Command Line Tool (SqlMetal.exe)
- Designer Wizard (.dbml)
- Manually (.cs)
The second option is the simplest but you don't have very much control of what gets generated. So, I favor creating my files manually, because I have total control and I only define what I need (Option 3).
Step 1: Define data class.
Make sure that you have a reference to System.Data.Linq.
| Customer Definition(partial) | |
[Table(Name="Customer")]
public class Customer
{
[Column(Name="Id", IsDbGenerated=true, DbType="int", IsPrimaryKey=true)]
public int Id
{
get;
set;
}
[Column(Name="FirstName")]
public string FirstName
{
get;
set;
}
}
|
To the left is my definition of my Customer table. What we essentially doing is marking up a class, that LINQ will use to communicate to our database. This class communicates several key pieces of information to us. The data table, fields and keys that we define as important.
So starting at the top, we have defined that our Customer class will talk to the Customer table. This could be a view or a stored procedure. The [Table(Name="Customer")] attribute handles this for us. We can do other advanced features, but we will talk about those at a different time.
Next, notice each property is marked with a [Column(Name=?)] column attribute. This allows LINQ to map your database column with your physical class properties. This allows the flexibility to have different database column names than what you want to expose to your code. There are additional attributes that allow you to set the Database type, and constraint information.
Last, you will notice that the Id field has some of those additional properties. We are telling LINQ that the Id field is an identity field, or SqlType "int" and is a primary key. If you want LINQ to handle some of your data input automatically, you have to be verbose on your Column Attribute definitions to handle your constraints and keys. If this case, since we setup keys on our tables and we want to use those keys to get data. We have to make sure that LINQ knows that and we will use this in the future.
|
Step 2: Create Data Context
In this step we are going to create a data context to handle our database interactions. First, we need to create a class that derives from System.Data.Linq.DataContext. Second, we create a static Create method to handle the initialization of the class for ease of use. Third, define tables, procedures and queries. Below show a simple class for our use.
public class InventoryDBDataContext : DataContext
{
public InventoryDBDataContext(string connection)
: base(connection)
{
}
public static InventoryDBDataContext Create()
{
string connection = ConfigurationManager.ConnectionStrings["InventoryDB"].ConnectionString;
return new InventoryDBDataContext(connection);
}
public Table<Customer> Customers;
}
You will notice that the static Create method allows for the hiding of implementation. In the next part you will see how this is helpful.
Step 3: Get Data
Well, we finally are ready to get data. To get a list of customer, all that we have to do is the following:
using (InventoryDBDataContext context = InventoryDBDataContext.Create())
{
var customers = (from c in context.Customers
select c).ToList();
Assert.IsTrue(customers.Count > 0);
}
Now you see that we are now containing the calls with a "using" statement. This allows for better garbage collection. With the 3.0 framework, you can allow a runtime definition of variables by just using the "var" keyword. The selection of the data looks very familiar with TSQL statements. The selection pieces defined:
| TSQL Query |
Definition |
| from c in context.Customers |
Search all customers from the Database. |
| select c |
return the selected customers as a whole Customer object. |
| .ToList() |
Immediately execute the LINQ query. |
We have now completed the basics with getting started with LINQ. Next time we will take a deeper look into LINQ queries and other customization of our classes.
Let me know what you think. Next time we will cover how to use XML LINQ in unit testing our code.
David Waddleton
Links:
I have been busy with my work @ Microsoft. Just in case you have not hear of what I am working on. My team is responsible for the new forums that Expressions is using. http://forums.expression.microsoft.com/en/forums Try it out and let us know what you think. A future feature that I am working on is the ability for you to consume data from the forums through a web service using WCF. In the past few weeks, I have been playing around with WCF quite a bit, with traditional ASP.NET, Silverlight 2.0 and with Ajax implementation. I will as I go along update you with how some of these feature work and some tips.
After building, a WCF service in Windows Server 2003, I thought no big deal, for Windows Server 2008. I deployed the service on my Windows Server 2008 machine and started getting 404.3 errors only with my WCF service. After checking that I had all of the necessary dependencies:
- 3.0 Framework
- WCF Activation installed
- This can be found under the 3.0 framework options. Depending on your operating system this could be under different locations.
- Vista : Control Panel -> Program and Features -> Turn on/ off Windows Features -> Microsoft.NET Framework 3.0
- Server 2008 : Server Manager -> Add Features-> .Net Framework 3.0 Features -> WCF Activation
I kept getting 404.3 errors, so I started examining IIS to make sure that the right stuff is registered. After not finding anything wrong, I started looking at the WCF installation. I found that my WCF services where not installed. You can do the installation manually by running "ServiceModelReg.exe", which was found at %Windows%\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\.
To check status run:
Command: ServiceModelReg -vi
Result: The expected result if "Default Installation" next to the features shown, otherwise the services are not active.
To turn on Services:
Command: ServiceModelReg -i
Result: Installation of WCF services in IIS 7(6)
I was then able to immediately test my service and see that I could get to the WSDL.
Hopefully this fixes your 404.3 issue.
David Waddleton
I came across the situation, where I needed to use an embedded resource file in a class library file. After spending much time, scratching my head trying to figure out how to get to the embedded resource. This is what I found.
First, you will need to use System.Reflection.Assembly. There are two important methods that are useful for what we need. GetManifestResourceNames and GetManifestResourceStream. The first method allow you to see what the names of the resources are that are part of your class.
Example: To get a list of assemblies in a current class library.
string[] resources = Assembly.GetCallingAssembly().GetManifestResourceNames();
After you know the name of the embedded resource, you can get a Stream for reading the resource using the GetManifestResourceStream method.
Second, to read resource files, you can use the System.Resources.ResourceReader class. The embedded resource file is a .resx resource file. The end result when the class is compiled, you will have the following reference as <Class Name>.<Name of Resource File>.resources. The compiled version automatically turns the resource in to a compiled version with a .resource extension. This can be done manually using the ResGen tool.
Sample Code:
/// <summary>
/// Internal member representation of Country List
/// </summary>
private static List<KeyValueStruct> countryList;
/// <summary>
/// Static Constructor, which will load the resource files
/// </summary>
static LanguagesCountries()
{
countryList = new List<KeyValueStruct>();
Stream stream = Assembly.GetCallingAssembly().GetManifestResourceStream("ResourceClass.Countries.resources");
ResourceReader rmCountries = new ResourceReader(stream);
PopulateCountries(rmCountries);
}
private static void PopulateCountries(ResourceReader reader)
{
foreach (DictionaryEntry entry in reader)
{
countryList.Add(new KeyValueStruct(entry.Key.ToString(), entry.Value.ToString()));
}
}
KeyValueStruct Structure for holding the values
/// <summary>
/// Structure to hold the Language Information
/// </summary>
public struct KeyValueStruct
{
private string key;
private string value;
/// <summary>
/// Constructor to load the Language / Value Pairs
/// </summary>
/// <param name="key">Language</param>
/// <param name="value">language code</param>
public KeyValueStruct(string key, string value)
{
this.key = key;
this.value = value;
}
/// <summary>
/// Language property
/// </summary>
public string Key
{ get { return this.key; } }
/// <summary>
/// Value Property
/// </summary>
public string Value
{ get { return this.value; } }
}
Have fun, let me know if this was helpful.
David Waddleton
del.icio.us tags:
Resource Files,
.NET
I am a bit late, but because of how busy I have been, I forgot to mention that ASP.NET AJAX has been released. Scott Guthrie has a great post with some great resources about what is new and resources to help you get started.
Links:
http://weblogs.asp.net/scottgu/archive/2007/01/23/asp-net-ajax-1-0-released.aspx
http://weblogs.asp.net/scottgu/archive/2007/01/25/links-to-asp-net-ajax-1-0-resources-and-answers-to-some-common-questions.aspx
Download the Bits:
http://ajax.asp.net/downloads/default.aspx?tabid=47
Remember, have lots of fun with this. I think you will enjoy it.
David
I say this cartoon, and I said "That is my dog!!".
Link to Get Fuzzy
Technorati tags:
Funny,
Dog,
Pet
The ASP.NET AJAX Beta is now live. This release includes a number of significant changes:
1) An update to http://www.asp.net/ and http://Ajax.asp.net/ to support the release
2) A comprehensive whitepaper detailing changes between the CTP’s and Beta
3) A simple to follow migration guide to help developers
4) Updated documentation
5) A revised ASP.NET AJAX Control Toolkit
6) And much, much more
This release is composed of three parts:
1) The Beta of the soon to be fully-supported ‘core’ ASP.NET AJAX functionality
2) A new CTP of additional functionality that runs on top of the Beta ‘core’
3) A new ASP.NET AJAX Control Toolkit that runs on top of the Beta ‘core’ and new CTP
Link to ASP.NET AJAX : The Official Microsoft ASP.NET AJAX Site
Technorati tags:
ASP.NET,
AJAX,
AJAX Beta
Wow, you have got to look at this!! The WPF team is making this training available free till the lauch of .NET Framework 3.0.
More Context: Collection 5134: Developing Rich Experiences with Microsoft® .NET Framework 3.0 and Visual Studio® 2005
This collection of 3 2-hour premium clinics teaches about the new capabilities provided by the .NET Framework 3.0. These clinics are for experienced Developers and Software Architects who are looking to adopt Microsoft's next generation technology within their solutions.
Topics covered within the collection include:
- Windows Presentation Foundation
- Windows Workflow Foundation
- Windows Communication Foundation
Link: https://www.microsoftelearning.com/eLearning/offerDetail.aspx?offerPriceId=109340
Well, in my current project, this question appeared very early on. Since, I am doing Test-Driven Development, I need to test my web service data layer, but I don’t need it all of the time. I used a pattern called Constructor based Dependency Injection. This allows you to replace the service with a different implementation. For my situation, the implementation is for removing an external dependency for testing. I removed this dependency, by mocking the object with NMock. NMock allows you to pass parameters, setup return values, exceptions and do sequence actions.
Download location: http://nmock.org/download.html
Now for the implementation, so here come the 2.0 Framework to the rescue. When you add a web service, a reference.cs or (reference.vb) file gets created in the directory with the web service WSDL. Fortunately, this class is created as a partial class; I used this knowledge to create another partial class that I can control. You don’t really want to update the reference.cs or .vb class from the web service, because it can change when you update the web service reference and all of your changes are gone. The summary below details the steps that are required to implement this.
Summary:
(Step 1, setup Constructor based Dependency Injection)
-
Create a partial class with the proper namespace and class name for your web service.
-
Define an interface for the methods that will be consumed from the web service.
-
Update the partial class in step 1, to use interface.
-
Implement Interface methods with this.<methodname> from web service.
-
Define a private member variable that will consume the Interface.
-
Create a constructor for your Web Service Data Access object that will use the interface.
(Step 2: Setup NMock and Mock service calls)
-
Create a mock object. (Ex: Mockery mock = new Mockery())
-
Create mock of Interface object: ( Ex: IMyService service = mock.NewMock<IMyService>(); )
-
Define what will be returned, or expected behavior. Expect.Once.On(service).Method(“MyCall”).Will(Return.Value(expected));
-
Verify that everthing worked correctly.mock.VerifyAllExpectationsHaveBeenMet();
// Provider that will consume the web service.
public class Provider
{
private IService apservice;
public Provider()
{
this.apservice = new MyWebService();
}
/// <summary>
/// Constructor that allows NMock testing
/// </summary>
/// <param name="service">Interface for Web Service</param>
public Provider(IMyService service)
{
this.apservice = service;
}
public string Method1(string name)
{
return apservice.MyCall(name,string.Empty);
}
}
// Interface for the Web Service
public interface IDiscussionService
{
Results MyCall(string param1,string param2);
}
// Partial class for the Web Service
public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol, IMyService
{
string IMyService.MyCall(string param1, string param2)
{
return this.MyCall(param1, param2);
}
}
//Unit Test that will NMock the web service[TestMethod] public void GetSitesTest() {
Mockery mock = new Mockery();IMyService service = mock.NewMock<IMyService>();
String expected = “Hello World”;Expect.Once.On(service).Method("MyCall").Will(Return.Value(expected));Provider target = new SiteProvider(service);
string actual = target.Method1(“Name”);
Assert.AreEqual(expected,actual);mock.VerifyAllExpectationsHaveBeenMet();
}
References:
Tags: NMock, Web Services, Injection, TDD
I would like to share some useful functions for you to use. This is part of an application that I will eventually share when I get it completed. In my last post, I showed how to create a FlowDocument from HTML, well I put that code into a function. The other function, takes a URL and takes the HTML and returns it as a string.
public static string ConvertUrlToHtmlText(string url) {
try {
HttpWebRequest request = HttpWebRequest.Create(new Uri(url)) as HttpWebRequest;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
StreamReader reader = new StreamReader(response.GetResponseStream());
string convertedText = reader.ReadToEnd();
return convertedText;
}
catch (WebException) {
return null;
}
catch (Exception) {
return null;
}
}
public static FlowDocument ConvertTextToFlowDocument(string text) {
try {
string xaml = HTMLConverter.HtmlToXamlConverter.ConvertHtmlToXaml(text, true);
return XamlReader.Load(new XmlTextReader(new StringReader(xaml))) as FlowDocument;
}
catch (Exception) {
return null;
}
}
The full code can be found at http://waddleton.net/Documents/HtmlConversion.zip
WinFx
Well, I have been playing with Avalon for a bit. One of the things that I have been playing with is a RSS viewer, just a little hobby. Viewing blogs were not difficult, but the displaying of the HTML from the RSS is where the problems start. But what you really need to do is use a converter and then put it into a FlowDocument. At the following location you can download a XAML/HTML Converter. http://wpf.netfx3.com/files/folders/developer/entry816.aspx
If you take this code and add it into a class library on it own. You can then do the following code:
string text = { Some HTML Text };
string xaml = HTMLConverter.HtmlToXamlConverter.ConvertHtmlToXaml(text, true);
rssBlogText.Document = XamlReader.Load(new XmlTextReader(new StringReader(xaml))) as FlowDocument;
This code is part of a UserControl where the Loaded event executes the above code.
This is the corresponding XAML code:
<FlowDocumentReader Name="rssBlogText" ViewingMode="Scroll"
HorizontalContentAlignment="Left" IsPageViewEnabled="False" IsPrintEnabled="False"
IsFindEnabled="False" IsScrollViewEnabled="True" IsTwoPageViewEnabled="False"
Zoom="75"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
This will allow you to see the resulting converted XAML in a FlowDocument. Try it and let me know.
Well, everyone it has been a while since I have blogged. I have not forgotten how. I just have been extremely busy. Have you gone out to the http://www.codeplex.com site? That was a project that I was working on. The is part of the reason for not blogging becuase I could not tell you about it until it was public. Take a look I know that you will like it.
Current Stuff: So I am done with that. Have you seen http://www.microsoft.com/rss/. That is another team with my group's work. So you are probably wondering what am I doing now. Well, I am working on creating WinFx clients and components. I am going to start sharing some of my experiences with WinFX (Avalon). I know that one of my stumbling block with such a new technology is that some of the examples are limited. I have put together a medium size application utilizing many new features and concepts. I will share some of what I have done in small pieces.
Here are some great resources for you get started with Avalon:
http://wpf.netfx3.com/
http://forums.microsoft.com/msdn/default.aspx?forumgroupid=24&siteid=1
http://msdn.microsoft.com/winfx/
Get the bit from here:
http://msdn.microsoft.com/windowsvista/downloads/products/getthebeta/