GeneralUpdate.Differential
Component Overview
GeneralUpdate.Differential is the core component responsible for binary differential updates in the GeneralUpdate framework. This component provides powerful differential algorithms that can accurately identify file changes between two versions, generate efficient incremental patch packages, and support patch restoration operations. By using differential updates, you can significantly reduce update package size and download time, making it particularly suitable for scenarios with frequent update releases.
Namespace: GeneralUpdate.Differential
Assembly: GeneralUpdate.Core.dll
public sealed class DifferentialCore
Core Features
1. Incremental Identification
- Accurately identify added, modified, and deleted files
- Intelligent file version comparison
- Support skipping specified files and formats
2. Binary Patch Generation
- Efficient binary differential algorithm
- Minimize patch file size
- Fast patch generation speed
3. Patch Restoration
- Safe patch application process
- Automatic handling of file dependencies
- Integrity verification mechanism
4. Blacklist Support
- File-level blacklist
- Format-level blacklist
- Flexible filtering rules
Quick Start
Installation
Install GeneralUpdate.Differential via NuGet (included in Core package):
dotnet add package GeneralUpdate.Core
Initialization and Usage
The following example demonstrates how to use DifferentialCore for incremental identification and patch operations:
using GeneralUpdate.Differential;
// Identify increments and generate binary patches
var sourcePath = @"D:\packet\app"; // Old version path
var targetPath = @"D:\packet\release"; // New version path
var patchPath = @"D:\packet\patch"; // Patch output path
await DifferentialCore.Instance?.Clean(sourcePath, targetPath, patchPath);
// Apply patches (restoration)
await DifferentialCore.Instance?.Dirty(sourcePath, patchPath);
Core API Reference
DifferentialCore Class
Instance Property
Get the singleton instance of DifferentialCore.
public static DifferentialCore Instance { get; }
Clean Method
Perform incremental identification, deleted file identification, and generate binary patch files.
Method Signature:
public async Task Clean(string sourcePath, string targetPath, string patchPath = null)
Parameters:
sourcePath: Path to the old version foldertargetPath: Path to the new version folderpatchPath: Directory to store discovered incremental update files (optional)
Function Description:
- Compare sourcePath and targetPath directories
- Identify added, modified, and deleted files
- Generate binary differential patches for modified files
- Save patches and added files to patchPath
Example:
// Generate patch package from v1.0.0 to v1.1.0
var source = @"D:\MyApp\v1.0.0";
var target = @"D:\MyApp\v1.1.0";
var patch = @"D:\MyApp\patches\v1.1.0";
await DifferentialCore.Instance.Clean(source, target, patch);
// Result: patch directory contains all necessary incremental update files
Dirty Method
Apply patches to update old version files to the new version.
Method Signature:
public async Task Dirty(string appPath, string patchPath)
Parameters:
appPath: Client application directory (current version)patchPath: Path to the patch files
Function Description:
- Read patch files from patchPath
- Apply patches to corresponding files in appPath
- Handle copying of added files
- Handle removal of deleted files
Example:
// Apply patches to current application
var appDir = AppDomain.CurrentDomain.BaseDirectory;
var patchDir = Path.Combine(appDir, "temp", "patches");
await DifferentialCore.Instance.Dirty(appDir, patchDir);
// Result: application updated to new version
Practical Usage Examples
Example 1: Basic Patch Generation
using GeneralUpdate.Differential;
public async Task GeneratePatchAsync()
{
try
{
// Version paths
var oldVersion = @"D:\MyApp\1.0.0";
var newVersion = @"D:\MyApp\1.0.1";
var patchOutput = @"D:\MyApp\patches\1.0.1";
Console.WriteLine("Starting patch generation...");
// Generate patches
await DifferentialCore.Instance.Clean(oldVersion, newVersion, patchOutput);
Console.WriteLine($"Patch generation complete! Output directory: {patchOutput}");
// Display patch information
var patchFiles = Directory.GetFiles(patchOutput, "*.*", SearchOption.AllDirectories);
Console.WriteLine($"Generated {patchFiles.Length} patch files");
long totalSize = patchFiles.Sum(f => new FileInfo(f).Length);
Console.WriteLine($"Total patch size: {totalSize / 1024.0:F2} KB");
}
catch (Exception ex)
{
Console.WriteLine($"Patch generation failed: {ex.Message}");
}
}
Example 2: Apply Patches
using GeneralUpdate.Differential;
public async Task ApplyPatchAsync()
{
try
{
// Application directory
var appDirectory = @"D:\MyApp\current";
// Patch directory
var patchDirectory = @"D:\MyApp\patches\1.0.1";
Console.WriteLine("Starting patch application...");
// Verify patch exists
if (!Directory.Exists(patchDirectory))
{
throw new DirectoryNotFoundException($"Patch directory not found: {patchDirectory}");
}
// Apply patches
await DifferentialCore.Instance.Dirty(appDirectory, patchDirectory);
Console.WriteLine("Patch applied successfully! Application updated to new version.");
}
catch (Exception ex)
{
Console.WriteLine($"Patch application failed: {ex.Message}");
}
}
Example 3: Complete Patch Workflow
using GeneralUpdate.Differential;
using System.IO.Compression;
public class PatchManager
{
// Generate and package patches
public async Task<string> CreatePatchPackageAsync(
string oldVersionPath,
string newVersionPath,
string outputPath)
{
try
{
// 1. Generate patch files
var tempPatchDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPatchDir);
Console.WriteLine($"Generating patches...");
await DifferentialCore.Instance.Clean(oldVersionPath, newVersionPath, tempPatchDir);
// 2. Compress patch files
var patchZipPath = Path.Combine(outputPath, "patch_1.0.1.zip");
Console.WriteLine($"Packaging patches...");
if (File.Exists(patchZipPath))
File.Delete(patchZipPath);
ZipFile.CreateFromDirectory(tempPatchDir, patchZipPath,
CompressionLevel.Optimal, false);
// 3. Clean up temp files
Directory.Delete(tempPatchDir, true);
var patchSize = new FileInfo(patchZipPath).Length;
Console.WriteLine($"Patch package created successfully: {patchZipPath}");
Console.WriteLine($"Patch package size: {patchSize / 1024.0:F2} KB");
return patchZipPath;
}
catch (Exception ex)
{
Console.WriteLine($"Patch package creation failed: {ex.Message}");
throw;
}
}
// Extract and apply patches
public async Task ApplyPatchPackageAsync(string appPath, string patchZipPath)
{
try
{
// 1. Extract patch package
var tempExtractDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempExtractDir);
Console.WriteLine($"Extracting patch package...");
ZipFile.ExtractToDirectory(patchZipPath, tempExtractDir);
// 2. Apply patches
Console.WriteLine($"Applying patches...");
await DifferentialCore.Instance.Dirty(appPath, tempExtractDir);
// 3. Clean up temp files
Directory.Delete(tempExtractDir, true);
Console.WriteLine($"Patch applied successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Patch package application failed: {ex.Message}");
throw;
}
}
}
// Usage example
var manager = new PatchManager();
// Create patch package
var patchZip = await manager.CreatePatchPackageAsync(
@"D:\MyApp\1.0.0",
@"D:\MyApp\1.0.1",
@"D:\MyApp\releases"
);
// Apply patch package
await manager.ApplyPatchPackageAsync(
@"D:\MyApp\current",
patchZip
);
Example 4: Patch Operations with Progress Display
using GeneralUpdate.Differential;
public class ProgressivePatchManager
{
public async Task GeneratePatchWithProgressAsync(
string sourcePath,
string targetPath,
string patchPath,
IProgress<string> progress)
{
try
{
progress?.Report("Starting file difference scan...");
// In actual scenarios, you can add progress reports before and after Clean
await DifferentialCore.Instance.Clean(sourcePath, targetPath, patchPath);
progress?.Report("Patch generation complete!");
// Statistics
var files = Directory.GetFiles(patchPath, "*.*", SearchOption.AllDirectories);
progress?.Report($"Generated {files.Length} patch files");
}
catch (Exception ex)
{
progress?.Report($"Error: {ex.Message}");
throw;
}
}
public async Task ApplyPatchWithProgressAsync(
string appPath,
string patchPath,
IProgress<string> progress)
{
try
{
progress?.Report("Starting patch application...");
await DifferentialCore.Instance.Dirty(appPath, patchPath);
progress?.Report("Patch applied successfully!");
}
catch (Exception ex)
{
progress?.Report($"Error: {ex.Message}");
throw;
}
}
}
// Usage example
var manager = new ProgressivePatchManager();
var progress = new Progress<string>(msg => Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {msg}"));
await manager.GeneratePatchWithProgressAsync(
@"D:\MyApp\1.0.0",
@"D:\MyApp\1.0.1",
@"D:\MyApp\patches\1.0.1",
progress
);
Notes and Warnings
⚠️ Important Notes
-
File Name Restrictions
- Cannot include files with the same name but different extensions (e.g., file.txt and file.log)
- Recommended to use unique file naming conventions
-
Directory Structure
- The relative structure of source and target directories should remain consistent
- Directory hierarchy is preserved during patch generation
-
Disk Space
- Ensure sufficient disk space for storing patch files
- Binary differential patches are usually smaller than complete files, but still require temporary space
-
File Locking
- When applying patches, ensure target files are not locked by other processes
- Recommended to apply patches after the application is closed
-
Backup Recommendations
- Recommended to backup original files before applying patches
- Can use ClientCore's BackUp option for automatic backup
💡 Best Practices
- Version Management: Maintain separate patch packages for each version for easier version tracking and rollback
- Patch Validation: Perform validation testing after generating patches to ensure they can be applied correctly
- Incremental Updates: Prioritize differential updates over full updates, which can save 50%-90% of download size
- Error Handling: Implement complete exception capture and error recovery mechanisms
- Performance Optimization: For large files, the performance advantage of the differential algorithm is more pronounced
🔍 How It Works
Clean Method Workflow:
- Scan all files in source and target directories
- Compare MD5 hash values of files to identify changes
- For modified files, use binary differential algorithm to generate patches
- For added files, copy directly to patch directory
- Record list of deleted files
Dirty Method Workflow:
- Read all files in patch directory
- For patch files, apply to corresponding original files
- For added files, copy directly to application directory
- Remove corresponding files according to deletion list
- Verify update integrity
Applicable Platforms
| Product | Version |
|---|---|
| .NET | 5, 6, 7, 8, 9 |
| .NET Framework | 4.6.1 |
| .NET Standard | 2.0 |
| .NET Core | 2.0 |
Related Resources
- Sample Code: View GitHub Examples
- Main Repository: GeneralUpdate Project
- Packaging Tool: The GeneralUpdate.PacketTool project depends on this component for differential packaging