Introduction
Welcome to Module 7 of our Complete C# Course: From Beginner to Advanced! After mastering collections and data structures in Module 6, it’s time to explore file handling and serialization to persist and manage data. In this module, we’ll cover reading and writing text files, working with streams, binary file handling, JSON and XML serialization/deserialization, and an in-depth look at System.IO classes. We’ll apply these concepts in a practical customer management system for a retail business, inspired by real-world CRM or e-commerce applications. With detailed examples, best practices, and pros/cons, you’ll learn to handle data persistence like a pro. Let’s dive in!
1. Reading and Writing Text Files
Text file operations involve reading from and writing to files using classes like StreamReader and StreamWriter.
Example: Customer Notes
using System;
using System.IO;
namespace CustomerSystem
{
class Program
{
static void Main(string[] args)
{
string filePath = "notes.txt";
// Writing to a text file
Console.Write("Enter customer note: ");
string note = Console.ReadLine();
File.WriteAllText(filePath, note);
Console.WriteLine("Note saved.");
// Reading from a text file
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine($"Stored note: {content}");
}
}
}
}
Real-World Use: Storing customer feedback or log messages in a CRM system.
Pros:
Simple API for basic file operations.
Text files are human-readable and widely compatible.
Cons:
Not efficient for large datasets.
No built-in structure for complex data.
Best Practices:
Use File.WriteAllText/ReadAllText for simple operations.
Check File.Exists before reading.
Handle exceptions for file access issues.
Alternatives:
Databases for structured data.
Binary files for performance.
2. Working with Streams
Streams provide a flexible way to read/write data sequentially using Stream, StreamReader, StreamWriter, etc.
Example: Streaming Customer Logs
using System;
using System.IO;
namespace CustomerSystem
{
class Program
{
static void Main(string[] args)
{
string filePath = "customer_logs.txt";
// Writing with StreamWriter
using (StreamWriter writer = new StreamWriter(filePath, append: true))
{
Console.Write("Enter log message: ");
string log = Console.ReadLine();
writer.WriteLine($"{DateTime.Now}: {log}");
}
Console.WriteLine("Log saved.");
// Reading with StreamReader
using (StreamReader reader = new StreamReader(filePath))
{
Console.WriteLine("Log contents:");
Console.WriteLine(reader.ReadToEnd());
}
}
}
}
Real-World Use: Logging user actions or streaming large datasets.
Pros:
Efficient for sequential data access.
using ensures resource cleanup.
Cons:
More complex than File methods.
Requires manual resource management without using.
Best Practices:
Always use using to dispose streams.
Use append: true for logging to avoid overwriting.
Buffer large reads/writes for performance.
Alternatives:
File class for simple operations.
Memory streams for in-memory data.
3. Binary File Handling
Binary files store data in a compact, non-human-readable format using BinaryReader and BinaryWriter.
Example: Customer Balance
using System;
using System.IO;
namespace CustomerSystem
{
class Program
{
static void Main(string[] args)
{
string filePath = "balance.dat";
// Writing binary data
using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
{
Console.Write("Enter customer ID: ");
string id = Console.ReadLine();
Console.Write("Enter balance: ");
double balance;
if (!double.TryParse(Console.ReadLine(), out balance))
{
Console.WriteLine("Invalid balance!");
return;
}
writer.Write(id);
writer.Write(balance);
}
// Reading binary data
using (BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
{
string id = reader.ReadString();
double balance = reader.ReadDouble();
Console.WriteLine($"Customer {id}: Balance {balance:C}");
}
}
}
}
Real-World Use: Storing compact data like financial records or game saves.
Pros:
Compact and efficient for structured data.
Faster than text files for large datasets.
Cons:
Not human-readable.
Requires precise read/write order.
Best Practices:
Use BinaryWriter/BinaryReader for structured data.
Validate data before writing.
Use using to manage resources.
Alternatives:
Text files for readability.
Serialization for complex objects.
4. JSON & XML Serialization/Deserialization
Serialization converts objects to a format (JSON/XML) for storage or transmission, while deserialization reconstructs objects.
JSON Serialization (Using System.Text.Json)
using System;
using System.IO;
using System.Text.Json;
namespace CustomerSystem
{
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public double Balance { get; set; }
}
class Program
{
static void Main(string[] args)
{
string filePath = "customer.json";
Customer customer = new Customer { Id = "C1", Name = "Alice", Balance = 1000.50 };
// Serialize to JSON
string json = JsonSerializer.Serialize(customer, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, json);
Console.WriteLine("Customer saved as JSON.");
// Deserialize from JSON
string jsonContent = File.ReadAllText(filePath);
Customer deserialized = JsonSerializer.Deserialize<Customer>(jsonContent);
Console.WriteLine($"Deserialized: {deserialized.Name}, {deserialized.Balance:C}");
}
}
}
XML Serialization (Using System.Xml.Serialization)
using System;
using System.IO;
using System.Xml.Serialization;
namespace CustomerSystem
{
[XmlRoot("Customer")]
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public double Balance { get; set; }
}
class Program
{
static void Main(string[] args)
{
string filePath = "customer.xml";
Customer customer = new Customer { Id = "C1", Name = "Alice", Balance = 1000.50 };
// Serialize to XML
XmlSerializer serializer = new XmlSerializer(typeof(Customer));
using (StreamWriter writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, customer);
}
Console.WriteLine("Customer saved as XML.");
// Deserialize from XML
using (StreamReader reader = new StreamReader(filePath))
{
Customer deserialized = (Customer)serializer.Deserialize(reader);
Console.WriteLine($"Deserialized: {deserialized.Name}, {deserialized.Balance:C}");
}
}
}
}
Real-World Use: Saving user profiles, exporting reports, or integrating with APIs.
Pros:
JSON: Lightweight, widely supported, human-readable.
XML: Structured, supports schemas, widely used in legacy systems.
Cons:
JSON: Limited to simple data structures.
XML: Verbose, slower to process.
Best Practices:
Use System.Text.Json for modern JSON handling (faster than Newtonsoft.Json).
Use XML attributes (e.g., [XmlRoot]) for customization.
Validate data before serialization.
Alternatives:
Newtonsoft.Json for advanced JSON features.
Binary serialization for compact storage (less common).
5. System.IO Classes in Depth
The System.IO namespace provides classes for file and directory operations.
Key Classes
File: Static methods for file operations (WriteAllText, ReadAllText).
FileInfo: Instance-based file operations with metadata.
Directory: Static methods for directory operations.
DirectoryInfo: Instance-based directory operations.
Stream: Base class for streams (FileStream, MemoryStream).
StreamReader/StreamWriter: Text-based stream operations.
BinaryReader/BinaryWriter: Binary stream operations.
Example: Directory and FileInfo
using System;
using System.IO;
namespace CustomerSystem
{
class Program
{
static void Main(string[] args)
{
string directoryPath = "CustomerData";
string filePath = Path.Combine(directoryPath, "info.txt");
// Create directory
Directory.CreateDirectory(directoryPath);
Console.WriteLine($"Created directory: {directoryPath}");
// Write file using FileInfo
FileInfo fileInfo = new FileInfo(filePath);
using (StreamWriter writer = fileInfo.CreateText())
{
writer.WriteLine("Customer Data: Sample");
}
// Read file metadata
Console.WriteLine($"File size: {fileInfo.Length} bytes");
Console.WriteLine($"Last modified: {fileInfo.LastWriteTime}");
}
}
}
Real-World Use: Managing file storage for logs, backups, or user data.
Pros:
Comprehensive API for file/directory operations.
FileInfo/DirectoryInfo provide rich metadata.
Cons:
FileInfo/DirectoryInfo are heavier than static methods.
Requires exception handling for file access issues.
Best Practices:
Use Path.Combine for cross-platform paths.
Use FileInfo for repeated file operations; File for one-off tasks.
Handle IOException and UnauthorizedAccessException.
Alternatives:
Databases for structured data.
Cloud storage (e.g., Azure Blob) for scalable storage.
Interactive Example: Customer Management System
Let’s build a console-based customer management system to apply file handling and serialization.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
// File: Models/Customer.cs
namespace CustomerSystem.Models
{
public record Customer(string Id, string Name, double Balance);
}
// File: Services/CustomerManager.cs
namespace CustomerSystem.Services
{
class CustomerManager
{
private readonly string filePath = "customers.json";
private List<Models.Customer> customers = new List<Models.Customer>();
public CustomerManager()
{
LoadCustomers();
}
public void AddCustomer(string id, string name, double balance)
{
try
{
if (balance < 0)
throw new ArgumentException("Balance cannot be negative.");
customers.Add(new Models.Customer(id, name, balance));
SaveCustomers();
Console.WriteLine($"Added customer: {name}");
}
catch (ArgumentException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
public void DisplayCustomers()
{
if (customers.Count == 0)
{
Console.WriteLine("No customers found.");
return;
}
foreach (var customer in customers)
{
Console.WriteLine($"{customer.Id}: {customer.Name}, Balance: {customer.Balance:C}");
}
}
private void SaveCustomers()
{
try
{
string json = JsonSerializer.Serialize(customers, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, json);
}
catch (IOException ex)
{
Console.WriteLine($"Error saving customers: {ex.Message}");
}
}
private void LoadCustomers()
{
try
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
customers = JsonSerializer.Deserialize<List<Models.Customer>>(json) ?? new List<Models.Customer>();
}
}
catch (IOException ex)
{
Console.WriteLine($"Error loading customers: {ex.Message}");
}
}
}
}
// File: Program.cs
namespace CustomerSystem
{
class Program
{
static void Main(string[] args)
{
Services.CustomerManager manager = new Services.CustomerManager();
while (true)
{
Console.WriteLine("\nCustomer Management System");
Console.WriteLine("1. Add Customer");
Console.WriteLine("2. Display Customers");
Console.WriteLine("3. Exit");
Console.Write("Choose an option: ");
string choice = Console.ReadLine();
if (choice == "3") break;
switch (choice)
{
case "1":
Console.Write("Enter customer ID: ");
string id = Console.ReadLine();
Console.Write("Enter customer name: ");
string name = Console.ReadLine();
Console.Write("Enter balance: ");
if (!double.TryParse(Console.ReadLine(), out double balance))
{
Console.WriteLine("Invalid balance!");
continue;
}
manager.AddCustomer(id, name, balance);
break;
case "2":
manager.DisplayCustomers();
break;
default:
Console.WriteLine("Invalid option!");
break;
}
}
}
}
}
How It Works:
Text Files: Could be used for logs (not shown here but similar to earlier examples).
Streams: Used indirectly via File.WriteAllText/ReadAllText.
Binary Files: Could store compact customer data (JSON used here for simplicity).
JSON Serialization: Saves/loads customers to/from customers.json.
System.IO: Manages file operations with error handling.
Why It’s Useful: Mimics CRM systems for managing customer data in retail or services.
Setup: Create a new Console App in Visual Studio or run dotnet new console. Organize files as shown. Requires System.Text.Json (included in .NET 6+).
Best Standards for Module 7
Text Files: Use for simple, human-readable data; prefer File for one-off tasks.
Streams: Use using for resource management; prefer streams for large or sequential data.
Binary Files: Use for compact, structured data; ensure read/write order matches.
Serialization: Use JSON for modern apps, XML for legacy systems; validate data before serialization.
System.IO: Use Path.Combine for paths; handle IO exceptions; prefer FileInfo for metadata.
Conclusion
You’ve just mastered file handling and serialization in C#! By learning to read/write text files, work with streams, handle binary files, serialize/deserialize JSON and XML, and use System.IO classes, you’re equipped to build data-driven applications. The customer management system demonstrates how these concepts power real-world solutions.
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam