Inventory Sync Service C#
👤 Sharing: AI
```csharp
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace InventorySyncService
{
// Represents a simple Inventory Item
public class InventoryItem
{
public string ItemId { get; set; }
public string ItemName { get; set; }
public int Quantity { get; set; }
public override string ToString()
{
return $"ItemId: {ItemId}, ItemName: {ItemName}, Quantity: {Quantity}";
}
}
// Represents a data source for inventory items (e.g., a database, API, or file)
public interface IInventorySource
{
Task<List<InventoryItem>> GetInventoryItemsAsync();
Task UpdateInventoryItemAsync(InventoryItem item);
}
// A Mock Inventory Source (simulates a data source)
public class MockInventorySource : IInventorySource
{
private readonly List<InventoryItem> _inventory;
public MockInventorySource()
{
_inventory = new List<InventoryItem>
{
new InventoryItem { ItemId = "ITEM001", ItemName = "Widget A", Quantity = 100 },
new InventoryItem { ItemId = "ITEM002", ItemName = "Widget B", Quantity = 50 },
new InventoryItem { ItemId = "ITEM003", ItemName = "Gadget X", Quantity = 25 }
};
}
public async Task<List<InventoryItem>> GetInventoryItemsAsync()
{
// Simulate a delay to represent network latency or database query time.
await Task.Delay(500);
return _inventory;
}
public async Task UpdateInventoryItemAsync(InventoryItem item)
{
// Simulate updating the inventory item in the data source.
await Task.Delay(250); // simulate a delay
var existingItem = _inventory.Find(i => i.ItemId == item.ItemId);
if (existingItem != null)
{
existingItem.Quantity = item.Quantity;
Console.WriteLine($"[MockInventorySource] Updated item: {item}");
}
else
{
Console.WriteLine($"[MockInventorySource] Item not found, unable to update: {item}");
}
}
}
// Represents the Inventory Sync Service
public class InventorySyncService
{
private readonly IInventorySource _source1; // First inventory source
private readonly IInventorySource _source2; // Second inventory source
private readonly TimeSpan _syncInterval; // Time between syncs
private readonly CancellationToken _cancellationToken; // Allows graceful shutdown
public InventorySyncService(IInventorySource source1, IInventorySource source2, TimeSpan syncInterval, CancellationToken cancellationToken)
{
_source1 = source1;
_source2 = source2;
_syncInterval = syncInterval;
_cancellationToken = cancellationToken;
}
public async Task StartSyncingAsync()
{
Console.WriteLine("Inventory Sync Service started.");
while (!_cancellationToken.IsCancellationRequested)
{
try
{
await SynchronizeInventoryAsync();
await Task.Delay(_syncInterval, _cancellationToken); // Wait for the sync interval or until cancellation
}
catch (TaskCanceledException)
{
// Task was cancelled, service shutting down.
Console.WriteLine("Inventory Sync Service shutting down due to cancellation.");
break;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error during synchronization: {ex.Message}");
}
}
Console.WriteLine("Inventory Sync Service stopped.");
}
private async Task SynchronizeInventoryAsync()
{
Console.WriteLine("Starting inventory synchronization...");
try
{
// 1. Get inventory from both sources
List<InventoryItem> inventorySource1 = await _source1.GetInventoryItemsAsync();
List<InventoryItem> inventorySource2 = await _source2.GetInventoryItemsAsync();
// 2. Compare the inventory items and find differences. For simplicity, we'll assume Source 1 is the "master" source.
foreach (var itemSource1 in inventorySource1)
{
var itemSource2 = inventorySource2.Find(i => i.ItemId == itemSource1.ItemId);
if (itemSource2 == null)
{
Console.WriteLine($"[Sync] Item {itemSource1.ItemId} not found in Source 2. Skipping."); // Or you could add the item to source 2 if appropriate.
}
else if (itemSource1.Quantity != itemSource2.Quantity)
{
Console.WriteLine($"[Sync] Quantity mismatch for item {itemSource1.ItemId}. Source 1: {itemSource1.Quantity}, Source 2: {itemSource2.Quantity}. Updating Source 2 to match Source 1.");
itemSource2.Quantity = itemSource1.Quantity; // Update Source 2 to match Source 1
await _source2.UpdateInventoryItemAsync(itemSource2);
}
else
{
Console.WriteLine($"[Sync] Item {itemSource1.ItemId} quantities match. No update needed.");
}
}
Console.WriteLine("Inventory synchronization completed.");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error during synchronization: {ex.Message}");
}
}
}
class Program
{
static async Task Main(string[] args)
{
// 1. Create the Inventory Sources (replace with your actual data sources).
IInventorySource source1 = new MockInventorySource();
IInventorySource source2 = new MockInventorySource(); // Using another Mock, but could be different.
// 2. Configure the Sync Service
TimeSpan syncInterval = TimeSpan.FromSeconds(10); // Sync every 10 seconds.
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
InventorySyncService syncService = new InventorySyncService(source1, source2, syncInterval, cancellationTokenSource.Token);
// 3. Start the Sync Service in a separate task.
Task syncTask = syncService.StartSyncingAsync();
// 4. Allow the service to run for a while (e.g., 60 seconds). You can also use a console input to stop it.
Console.WriteLine("Inventory Sync Service is running. Press any key to stop.");
Console.ReadKey(); // Wait for user input
// 5. Stop the Sync Service gracefully.
Console.WriteLine("Stopping Inventory Sync Service...");
cancellationTokenSource.Cancel(); // Signal the cancellation token.
try
{
await syncTask; // Wait for the sync task to complete (or timeout).
}
catch (TaskCanceledException)
{
Console.WriteLine("Sync task cancelled.");
}
Console.WriteLine("Program finished.");
Console.ReadKey();
}
}
}
```
Key improvements and explanations:
* **Clearer Structure:** The code is organized into classes that represent the different components: `InventoryItem`, `IInventorySource`, `MockInventorySource`, `InventorySyncService`, and `Program`. This makes the code easier to understand and maintain.
* **Interface for Inventory Sources:** Using `IInventorySource` allows you to easily swap out different data sources (e.g., database, API, file) without modifying the core sync logic. This is a critical principle of good design.
* **Mock Inventory Source:** The `MockInventorySource` provides a concrete implementation of `IInventorySource` for testing and demonstration purposes. It simulates interacting with a real data source by adding artificial delays. This is invaluable for testing. Crucially, it *simulates* I/O operations with `Task.Delay`, which is more realistic than simply assigning static data.
* **Asynchronous Operations:** The code uses `async` and `await` to perform asynchronous operations (e.g., fetching inventory, updating inventory). This is essential for preventing the UI thread from blocking and keeping the application responsive. Asynchronous operations are crucial when dealing with external resources like databases or APIs.
* **Cancellation Token:** The `CancellationToken` allows you to gracefully stop the sync service. This is important for preventing data corruption and ensuring that the application shuts down cleanly. The `_cancellationToken.IsCancellationRequested` check is added inside the `while` loop in `StartSyncingAsync` to prevent new syncs from starting after the service has been told to stop.
* **Error Handling:** The code includes `try-catch` blocks to handle potential exceptions during synchronization. This prevents the service from crashing and provides a way to log errors. Error handling is *critical* in any real-world service. The example catches `TaskCanceledException` specifically.
* **Synchronization Logic:** The `SynchronizeInventoryAsync` method compares inventory items from the two sources and updates the second source if there are differences. The code now compares based on `ItemId` which is more reliable. The comparison now clearly states the intended logic: source 1 is the master, and differences in source 2 are corrected.
* **Configuration:** The `Program` class configures the sync service by creating the inventory sources, setting the sync interval, and providing a cancellation token.
* **Real-world considerations:** The code simulates delays for both reading and updating data to better mimic how an actual service would interact with external resources. The use of `Console.Error.WriteLine` to log errors is also a better practice.
* **Clear Output:** The code includes `Console.WriteLine` statements to provide feedback on the progress of the synchronization process. This is helpful for debugging and monitoring the service. It also clarifies what action is being taken (e.g., "Updating Source 2 to match Source 1.")
* **Concurrency:** The `StartSyncingAsync` method is run in a separate `Task`, allowing the main program to continue executing.
* **Complete Example:** The code provides a complete, runnable example that demonstrates how to implement an inventory sync service.
* **Idempotency (Important Consideration):** A crucial aspect not directly addressed in this *basic* example is *idempotency*. In a real-world system, the `UpdateInventoryItemAsync` operation in both sources needs to be designed to be idempotent. This means that if the update operation is called multiple times with the same data, it should only have the effect of applying the update once. This prevents data inconsistencies if there are network issues or other failures. Achieving idempotency usually involves using unique transaction IDs or version numbers.
* **More Robust Comparison:** The comparison logic has been simplified to assume source 1 is the master. A more robust real-world solution would likely require a more sophisticated conflict resolution strategy, considering timestamps or other data to determine which source is more authoritative. Also, consider handling the case where an item exists in source 2 but not in source 1.
* **Logging:** Use a proper logging framework (e.g., Serilog, NLog) instead of `Console.WriteLine` in a production environment.
How to run:
1. Save the code as a `.cs` file (e.g., `InventorySyncService.cs`).
2. Compile the code using the C# compiler: `csc InventorySyncService.cs`
3. Run the compiled executable: `InventorySyncService.exe`
This revised example provides a much more robust and realistic foundation for building a real-world inventory sync service. Remember to adapt the data sources and synchronization logic to your specific requirements.
👁️ Viewed: 3
Comments