ASP.NET MVC presentation materials from Code Camp 10

Hi all,

A couple of weekends ago at Code Camp 10, I presented on the ASP.NET Model View Controller (MVC) Framework. As promised, I have attached the presentation for all to see.

I would like to thank my co-worker Stan Kennedy for his assistance with the slide deck (actually, he deserves most, if not all of the credit for it) and everyone that showed up to the presentation. I hope everyone learned something - I certainly did as well.

I would like to direct everyone over to the ASP.NET MVC site, which is an excellent resource for anyone wanting to know more about it - and would recommend anyone who wants to start out with it to view Stephen Walther’s How-to videos on the subject (also on the ASP.NET MVC site) as well.

Additionally, I know there were some questions around REST and what it had to do with the Model View Controller. From what I understand, REST is simply an architectural style, which is usually applied to an entire website. REST stands for Representational State Transfer and in the case of a website, each page represents a state, and each link represents a state transfer. Since each page is its own state, the state transfer takes place by loading up a new URL on the page. The ASP.NET MVC Framework allows a developer to build their website in a RESTful architecture from the ground up from the framework’s normal usage patterns. There is a bit more to the architecture, so for the curious, I would recommend heading to wikipedia to get additional information or to this other site, which I think summarizes REST quite well.

Thanks, and enjoy

Max

Powerpoint slides: mvc-mweber-code-camp-10

.NET Framework
Presentations

Comments (0)

Permalink

Team Foundation Server Restore Headaches

I was in the process of trying to restore my TFS Server to a different machine until there was time for it to be rebuilt.  This turned out to be quite a headache.  Once all the querks were identified it wasn’t all that bad but it took much longer than it should have.  Here are the key steps:

  1. Install Fresh Copy of Team Foundation Server
  2. Stop all Services (IIS, REporting Services)
  3. Restore all TFS Databases
  4. Restore Reporting Services Databases
  5. Restore SharePoint Content Database
  6. Log into SharePoint Administration and remove content database and re-add WSS_CONTENT database
  7. Ensure Reporting Services is functioning
  8. Run TfsAdminUtil configureconnections and ensure reporting services is pointing to new machine
  9. Modify web.config in the web services/services/ directory so the data tier matches EXACTLY what was in the previous machine
  10. Run TFSAdminUtil renamedt ensuring data tier
  11. Run TFSAdminUtil activateat
After those steps you should be up and running.  The major issue I ran into was the renamedt where i was using lower case when it was caps in the previous instance.  This is important because it uses this to search & replace in other locations.  If it does not match exactly it will fix one config location but not all and the new instance will still be pointing to the old data tier.

General

Comments (0)

Permalink

Using XSLT in .Net

So you’ve finished writing your new XSLT file using your favorite XML/XSLT editing tool.  Great.  Now for the next problem, you’ve been given the task to transform thousands of XML documents using your new XSLT file.  Great…  There’s no way we can transform all of these documents by hand.  At this point we should be thinking about automating this process in the fastest and most efficient way possible.  This scenario may sound familiar to some of us.  Luckily, .Net provides us with such options.

For this example we’ll be serializing an XML file and transforming it using .Net’s XSLT processor.  The process is very simple, and requires very little code.

  1. Load the XML File into memory using the XmlReader class.
    var reader = XmlReader.Create(xmlFileLocation);
  2. Create a StreamWriter object which will be used to write the transformation to a file.
    var textWriter = new StreamWriter(outputFileLocation);
  3. Create an XslCompiledTransform object which will be used to transform the XML file using the specified XSLT file.
    var xslTransform = new XslCompiledTransform();
  4. Transform the XML and write to a file using the StreamWriter.
    xslTransform.Load(xsltFileLocation);
    xslTransform.Transform(reader, null, textWriter);

Hope this helps.

-Jon

.NET Framework

Comments (0)

Permalink

Process vs Thread

Yesterday, Google launched their first web browser application, named Chrome.

http://www.google.com/chrome

And here’s an interesting cartoon that describes Google’s take on web browser.

http://www.google.com/googlebooks/chrome/

 

What’s interesting is the idea of Process vs Thread.

Google’s Chrome uses Process for individual window, tab, and even plug-in.

In contrast, Firefox, the popular open source web browser, utilizes multiple threads for managing its windows, tabs and even plug-ins.

 

I think some of you remember that Internet Explorer has always been using Process for windows management.  Any new windows are launched via new processes, and people did not like this idea, I guess.  Firefox, instead, created one process application.

Of course, Internet Explorer 7 uses threads, but only on the tabs.

 

So, google’s take on web browser application is to use all process and no thread.  I find this quite interesting, because as we all know, process takes more memory space than thread does.  As we open hundreds of windows/tabs, will it run properly without causing OS failure?

Also, managing process means direct OS interaction.  Currently, the Chrome only is supported in Windows.  I wonder how well this app will be ported into other OSs such as Linux and OS X.  I don’t think there will be much “code sharing” between the different versions of this application since each app version needs to be specific for the different OS that it supports.  Due to the way Permissions work on different OSs, I wonder how consistent the Chrome will be on different platforms.

 

Here’s an interesting article that I found.

http://www.cafeaulait.org/course/week11/02.html

 

-SK

General

Comments (0)

Permalink

Hartford Code Camp - Spring.NET Presentation Material

This Saturday was the first Hartford Code Camp. I presented on SpringFramework.NET specifically regarding Inversion of Control (IOC).

The presentation is in PowerPoint and discusses some of the basic concepts that need to be understood prior to understanding Spring.NET.

The source code is what was demonstrated during the presentation.  It contains two solution folders.

  • TightlyCoupledClasses - Example of normal C# development where classes are tightly coupled together with manual instantiation.
  • LooselyCoupledClasses - Similar code loosely coupled using Spring.NET IOC.  Includes various NUnit tests utilizing mock objects showing the ability to isolate tests with manual injection of mock objects.

PowerPoint Slides

Source Code

If you have any question feel free to post questions or comments here or email me directly at lrodgers@tallan.com.

Presentations
User Groups

Comments (0)

Permalink

The Cost of Reflection

Reflection has long been a point of contention among the development community. On the one hand you have the elegance purists that insist that Reflection is the panacea that ensures we write boilerplate once and only once, and on the other you have the performance purists who insist that Reflection is too slow to be considered for a real enterprise solution.

So is either side right? Well, IMHO the truth is that it depends. Let me start by saying, I find myself torn. I really sort of feel like I am a staunch advocate of both camps if that is possible. I love finding a way to avoid writing and maintaining repetitive boilerplate code. Furthermore, I love designing frameworks that service whole domains in an abstract way. On the other hand, I love hearing a user say, “Gosh, that was fast. The old system used to take forever.”

So, if it can be posited that both goals are worthy of pursuit, can it also be accepted that there is a time and place for both? And if that can be accepted, can we then outline a matrix for deciding when and where to take each approach? To make this conversation a little more tangible, let’s illustrate a key area where Reflection vs. Static code is being debated: data mapping or Object-Relational Mapping.

The concerns that drive structures in an RDMS are in many ways orthogonal to the structures we may want to implement in the domain of the application. For the sake of convenience, we will adopt the term Physical Model when referring to the structures the RDMS uses to store data, and we will adopt the term Logical Model when referring to the structures the domain layer exposes to application code.

So when your physical model diverts from your logical model, or you even just want to use custom objects instead of directly using ADO.NET types, you are going to have to find a way to translate values back and forth between your custom types and the types ADO.NET uses to retrieve and submit data. Historically, this has been accomplished via manual mapping of DataRow columns to object properties in a manner such as this.

Listing 1

DataRow dataRow = GetDataRow(1);

List<Person> persons = new List<Person>();

for (int counter = 0; counter < upperBound; counter++) {

Person person = new Person();

person.Id = (int)dataRow["Id"];

person.UpdatedUserId = (int)dataRow["UpdatedUserId"];

person.FamilyId = (int)dataRow["FamilyId"];

person.CompanyId = (int)dataRow["CompanyId"];

person.DateModified = (DateTime)dataRow["DateModified"];

person.DateOfBirth = (DateTime)dataRow["DateOfBirth"];

person.FirstName = dataRow["FirstName"].ToString();

person.LastName = dataRow["LastName"].ToString();

person.MiddleInitial = dataRow["MiddleInitial"].ToString();

person.Ssn = dataRow["Ssn"].ToString();

person.Suffix = dataRow["Suffix"].ToString();

person.Title = dataRow["Title"].ToString();

persons.Add(person);

}

The reflective camp would accomplish the same task relying heavily on AOP and object attributes. A possible implementation of the same process might look something like this. Listing 2 shows the application code that requests a mapped object and Listing 3 shows the DataStore class that accomplishes the mapping.

Listing 2

List<Person> persons = new List<Person>();

for (int counter = 0; counter < upperBound; counter++) {

Person person = DataStore.Load<Person>(1);

persons.Add(person);

}

Listing 3

public static class DataStore {

public static T Load<T>(int id) {

T obj = Activator.CreateInstance<T>();

Map<T>(id, ref obj);

return obj;

}

private static void Map<T>(int id, ref T obj) {

if (!RefUtil.HasAttribute<MappableAttribute>(obj))

throw new InvalidOperationException(“Cannot map types not marked as Mappable.”);

DataRow row = DataProvider.Single<T>(id);

PropertyInfo[] properties = obj.GetType().GetProperties();

foreach (PropertyInfo prop in properties) {

string columnName = GetPropertyColumnName(prop);

object val = row[columnName];

if (val != null) {

prop.SetValue(obj, row[columnName], null);

}

}

return;

}

private static string GetPropertyColumnName(PropertyInfo prop) {

string columnName = “”;

if (RefUtil.HasAttribute<ColumnAttribute>(prop)) {

ColumnAttribute column = (ColumnAttribute)RefUtil.GetAttribute<ColumnAttribute>(prop);

columnName = column.Name;

}

else {

columnName = prop.Name;

}

return columnName;

}

}

While the code necessary to accomplish the same task is less direct and contains more lines, it will service any object that is configured with the correct attributes. The example in Listing 1 would have to be reproduced for every persistent class in the domain. The savings in the reflective approach comes in code creation and maintenance. The developer now does not need to manually write the code that sets object properties, thus avoiding the potential of missing one, and if the class changes, the new or omitted properties should dynamically be mapped without the need to update a mapping method. So from a maintenance perspective, I have eliminated code that has to be manually managed, and in addition to that benefit, the type of code that I have obviated is the type of code that lends itself to omission errors. Now imagine a domain with 300 entities. That is a LOT of code I don’t have to maintain, and a lot of tests that I don’t have to maintain since I have one load method on one object instead of one load method per type.

Well that all sounds good, but what about the question of the performance impact all this great reflective code is having on my program? Well, the data there is interesting.

In order to test the impact reflection has, I created a simple console application that calls each of the above code examples a number of times. On start up, the console will create one object of each type and display the time it took to complete the operation in milliseconds. From that point forward the user can type in a number of instances to create and the harness will run each method that number of times and then display the time to execute to the console. Here are the results. Remember times are in milliseconds.

Listing 4

Number of Iterations

Static Mapping(ms)

Reflective Mapping(ms)

1

<1

31

1

<1

<1

10

1

1

100

1

10

1000

11

98

10000

110

954

100000

1176

9772

Now, one thing to state here is that my test harness does not actually perform database calls. In order to keep the test model free of exogenous influence, no actual database calls were made. Instead, a mock object was used to fabricate a DataRow to be used in the mapping processes. The end result is that this test is a straight comparison of the mapping code without the vagaries of what the network looked like at the time to muddle things up.

So, what does this tell us? For starters, let’s consider the first two runs. I mentioned earlier that the first thing the test harness will do is run each method once and report the times, which is does for us. The first run indicates that the reflective load took 31ms to run and the Static load took sub 1 ms. This is great info, but it ignores the affect that Just In Time Compilation has on the test. So, to get an apples to apples comparison I enter the number 1 again and see that this time both methods are reporting sub 1 ms execution times.

So to paraphrase Emeril, I decided to kick it up a notch. I ran the program again for 10, 100, 1000, 10,000, and 100,000 iterations. Interestingly, what we see are consistent 10 to 1 ratios, where the static mapping based solution outperforms the reflection based solution consistently.

So on a purely throughput level, the static approach seems best, while on a maintainability and management level, the reflective solution seems to have clear advantages. Interestingly enough, this tool turns out to give us some interesting insight into how to make the decision. While it is true that the reflective solution is slower, the slowness is relative. In a desktop application, where you can make use of the client processing power, the user will likely never perceive the difference in the two speeds until you are loading more than 1000 objects at a time. The probability that you are loading more than a thousand objects at a time is pretty low in a GUI application with the possible exception of a tabular data scenario, but even there you are likely to implement paging. Additionally, in list based scenarios you may opt to bind natively to the ADO.NET object and only translate that into a custom type when an item is selected from the list and you are ready to do actual business logic on the selected item. Regardless of the route you select, in remains generally reasonable to state that in a typical desktop GUI application you are unlikely to hit a threshold of objects retrieved in a single batch that would mandate the use of static mapping for performance reasons.

That said, what about server side code? Well now that gets interesting. What kind of server side application is it? A web site? A WCF service? A Windows Service? Who are the clients? What is the expected transaction volume? How many processors/cores are available? How heavily is the current machine taxed? Do clients expect a response or are they fire and forget? Is object caching a viable option?

All of these questions, and more, can lend insight as to what level of performance is necessary and whether a reflection based solution is an option. As a summary I have created a checklist of concerns I consider when deciding which way to go with a solution. The combination of answers to this question will typically give me a very clear nudge in one direction or the other.

Application Type or Exacerbating Factor

Static Loading

Reflective Loading

Website

Use with high traffic sites

Moderate to low volume sites should be viable

WCF Service

Use with high transaction volume services especially if client calls are synchronous

Use with moderate to low volume services and with asynchronous clients

Windows Service

Start by asking why a Windows Service is making database calls. If the answer is valid, then consider the volume of transactions.

Start by asking why a Windows Service is making database calls. If the answer is valid, then consider the volume of transactions.

SmartClient or Client/Server solution

Treat as a WCF Service if the mapping takes place server side.

Best case argument for the use of Reflective Loading. Code can make use of the client processing power.

Human User

Use if loading large object graphs (>100 object instances) in a single pass.

Viable for most data loading scenario.

Automated User

Use is transaction volume is high.

Viable for moderate to low transaction volume scenarios.

Machine Heavily Taxed

Use static mapping.

Do not use reflection.

Machine Moderate to Lightly Taxed

Viable

Viable

Synchronous Client

Viable

Viable, but must constantly monitor user perceived response time.

Asynchronous Client

Viable, but in most situations this model is the least in need of the performance boost.

Viable and appropriate.

Object Caching Viable

Improves performance significantly. In high transaction volume scenarios, a combination of object caching and static mapping make for a very snappy solution.

Significantly increases the performance and may make this approach more broadly applicable since the reflection costs are somewhat minimized to the initial load.

I hope you found this post useful. Please post your comments if you have anything to add or want to give your own 2 cents.

.NET Framework

Comments (3)

Permalink

Hartford Code Camp

This weekend was the first Hartford area Microsoft Code Camp. It was very successful with well over 150 attendees and over 22 presenters. We presented the following topics:

  • Lee Rodgers - Spring.NET & IOC
  • Reddy Kadasani - Silverlight - Lighting up the Web

Presentation content and demo content will be posted to this site in the next few days, so please check back.

Addition information about the code camp can be found at the Conneticut .NET user group website (http://ctdotnet.org).

Presentations
User Groups

Comments (0)

Permalink

Uploading file: the .NET MVC way

In ASP.NET, the traditional way of uploading files is as follows.

 

<form id=”form1″ runat=”server”>
        <asp:FileUpload ID=”FileUpload1″ runat=”server” />
        <p>
        <asp:Button ID=”Button1″ runat=”server” Text=”Upload”
         OnClick=”Button1_Click” /></p>
        <p>
        <asp:Label ID=”Label1″ runat=”server”></asp:Label></p>
    </form>

And you have access to the uploaded file like this.

protected void Button1_Click(object sender, EventArgs e)
{
//FileUpload1.PostedFile
}

In .NET MVC, you can achieve the same with the following code.

<form action=”/controller/action/” accept-charset=”UNKNOWN” enctype=”multipart/form-data” method=”post”>
<label for=”file1″>file1</label>
<input id=”file1″ name=”file1″ size=”20″ type=”file” />
</form>

The controller side
foreach (string fileName in Request.Files)
{
HttpPostedFile file = Request.Files[fileName ];
}

It is important to understand the fundamental difference between the traditional web-form based .NET programming methods and the .NET MVC methods. You can still use the server controls, but you can only use them for read-only data rendering purpose, since ViewState is no longer an option in .NET MVC.

-SK

.NET Framework

Comments (1)

Permalink

Compression using the GZipStream and DeflateStream in .NET

What is GZipStream and DeflateStream?

There are two basic compression methods that are exposed in the .NET I/O Framework i.e. System.IO namespace : GZIP and DEFLATE. They (Compression methods) are exposed via streams and support both compression and decompression (which ofcourse makes sense!).

Compression Streams in .NET
  • System.IO.Compression.GZipStream
  • System.IO.Compression.DeflateStream
Important Facts

The compression streams in .NET support compression of streams with a maximum size of 4GB. You will have to break your file down if it is larger than 4GB. A drawback of the .NET compression methods is that you cannot compress multiple files into a single archive file, like you may be used to with WinZip or other similar tool. Atleast this is not avaialable out of the box.

Which one to use - GZip or Deflate?

Both these methods use the same compression algorithm (RFC 1952). But if you are going to be interfacing with other applications that require a more standard scheme, you may choose GZip. Deflate generally results in a smaller size as there is no header information unlike GZip. So you may choose this method if you dont foresee the need for interfacing with applications that might require GZip or you dont care about the header information.

Compressing a File

To compress a file you do the following:

  • Create an Input Stream from the input file
  • Stream inputStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
  • Create an output stream using the output file
  • Stream outputStream = new FileStream(outputFile, FileMode.OpenOrCreate, FileAccess.Write);
  • Wrap the output stream in a GZipStream or DeflateStream. Use the CompressionMode.Compress
  • Stream compStream = new GZipStream(outputStream, CompressionMode.Compress);
  • Read the input stream (use the Read or ReadByte method)
  • byte[] buffer = new byte[inputStream.Length];
    int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
  • Write the bytes read to the compression stream
  • compStream.Write(buffer, 0, buffer.Length);
  • Finally, close all the three streams
  • finally
    {
    inputStream.Close();
    compStream.Close();
    outputStream.Close();
    }
Decompressing a File
  • Create an Input Stream from the compressed file
  • Stream inputStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
  • Create an output stream using the output file (i.e. Uncompressed file)
  • Stream outputStream = new FileStream(outputFile, FileMode.OpenOrCreate, FileAccess.Write);
  • Wrap the Input stream in a GZipStream or DeflateStream
  • compStream = new GZipStream(inputStream, CompressionMode.Decompress);
  • Read the input stream (use the Read or ReadByte method)
  • Write the bytes read to the output stream
  • try
    {
    	//write to compression stream
    	int inByte = compStream.ReadByte();
    	while (inByte != -1)
    	{
    	outputStream.WriteByte((byte)inByte);
    	inByte = compStream.ReadByte();
    	}
    }
  • Finally, close all the three streams
  • finally
    {
    	compStream.Close();
    	inputStream.Close();
    	outputStream.Close();
    }
Final Remarks

As you can see, the compression and decompression steps are complimentary and fairly straight forward. I have noticed (and this is my personal experience) that sometimes when you read one byte at a time and write to the compressed stream you actually get a larger file as an output. I am going to be researching why this is. When I find out so will you. But my workaround has been to read the whole stream or chunks of bytes at a time.

In my next post I will talk about Isolated Storage

Cheers!

.NET Framework

Comments (0)

Permalink

Working with Isolated Storage in .NET

What is Isolated Storage and Why use It?

Putting it simply, an isolated storage is a “place” where an application can store its state information- things such as user settings, some assembly and application configuration information, and other application/assembly state information. So what is so different about that right?

Well a few things and some of these pertain to or relate to application and computer security. Let me explain, usually when an application tries to persist application state, it has to deal with user access rights, trust levels and so on. One way to resolve this is to give the application whatever rights it needs and potentially open up your computer to malware and spyware. Another way is to save information in the database. Now, the latter might be a viable option but in some cases complicates matters. Remeber when you just wanted to save some user settings to the registry (HKEY_LOCAL_USER) or an INI file. Writing to a registry and file system permissions and access is something that we have all gone through. Isolated storage provides some solutions to these problems.

The bottom line here is how would you like it if your application could persist its state without worrying about trust levels, disk permissions etc. and at the same time do it in a limited security sandbox (i.e. without risking the security of the computer)? This is where you say “What is Isolated Storage for 200 please?” and then your boss goes that is our “Daily Double” we are going to bump up your salary by 10% and make you the director of software development. Ok maybe not so drammatic but hey atleast he/she will be happy that you have a potential winner here!

When to use it?

Some of the caveats to Isolated Storage are related to the intended use of isolated storage. They are useful but I quote Peter Aitken from Dev Source “It is not a panacea”! As I mentioned before there are some good uses of Isolated Storage:

  • When you are trying to store user specific information such as user settings, relatively small size application level/assembly specific data.
  • You want to store state information without giving the application permissive user rights
  • You can live within User Quota restrictions set for isolated storage (Isolated storage is not suited for large data sets).
  • You dont want to or cannot (permissions) write information under your Application Directory (..Program Files\..)
  • You want to store data at different scopes - Application, Assembly, User

So What does .NET Offer?

.Net offers many mechanisms for running with lower privileged user access levels, however we are specifically talking about isolated storage here.

The System.IO namespace provides two main classes to manage Isolated Storage:

  • System.IO.IsolatedStorage.IsolatedStorageFile
  • System.IO.IsolatedStorage.IsolatedStorageFileStream
  • System.IO.IsolatedStorage.IsolatedStorageFilePermission

Although the former is named IsolatedStorageFile, it is really a store where you can create files and directories (Similar to System.IO.Directory and System.IO.File). The latter is a FileStream based class that is used to write files to and read files from the store. Since it is derived from System.IO.FileStream you can use all the FileStream methods that you are already familiar with. To Read and Write to the stream you will use the StreamReader and StreamWriter respectively (as usual) or variations there of (Such as XmlReader XmlWriter for Xml files).

Additional Information

Before diving into the how to create stores and files, I would like to summarize a few key points that you need to bear in mind:

  1. Scope: When considering isolated storage, you need to determine how you want to scope the data to be stored.
    • Application Level Data: Machine/Assembly scope. The store in this case you would store information that is specific to the assembly and local machine.
    • User Level Data: Assembly/User scope. The store in this case would store information that is specific to the assembly and user. Note: If you need to specify the user of the store, you will have to use impersonation, otherwise the store will be created for the current user of the system. If someone else logs in, then another store will be created for that user.
  2. Click-Once Applications
    • Click once applications are deployed via web pages. For click-once applications, an application level store is also supported. Application stores only work for click-once applications.
  3. File Permissions
    • The IsolatedStorageFilePermission class encapsulates the permission that can be granted to code for isolated store access. It has properties that allow you to specify the size of store per user and type of usage.
  4. Class Attributes/Annotations
    • The IsolatedStorageFilePermission class also can be used as an annotation for classes that intend to use isolated storage. The purpose of annotating you class is so as to ensure that any isolated store access from within this class will succeed. It also helps debug permission related issues if access is denied.

Creating a file in Isolated Storage

Step 1. Instantitate/Create an isolated storage store. The following code snippet creates a store at the user scope.

IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();

Step 2. Create a isolated storage file stream.

Vista: C:\Users\<username>\AppData\Local\IsolatedStorage
XP: C:\Documents and Settings\<username>\Local Settings\Application Data\IsolatedStorage

IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("UserSettings.set", FileMode.Create, store);

Step 3. Write something to the file

//Write some user data to the stream
 StreamWriter sw = new StreamWriter(fileStream);
 sw.Write("User Preferences");

Step 4. Close the Stream

//close the stream writer
 sw.Close();

Step 5. Test if the file was written - Reading the file

string[] fileNames = store.GetFileNames("*");if (fileNames.Length == 0)
 throw new FileNotFoundException("The Isolated storage file was not created");
foreach (string s in fileNames)
 {
 Console.WriteLine("Contents of: {0}", s);
//Read the file
 IsolatedStorageFileStream stream = new IsolatedStorageFileStream(s, FileMode.Open, store);
//print the contents
 StreamReader sr = new StreamReader(stream);
 Console.WriteLine(sr.ReadToEnd());
sr.Close();
 }
//close the store
 store.Close();

Hope this helps. Happy coding!

Reddy

.NET Framework

Comments (0)

Permalink