---
sidebar_position: 3
title: Creating a data service
---

As we previously added the addition of an item, we now have two places in the code in our code where we access our local storage.

In order to simplify the management of our code, we will therefore use the IOC & DI.

In our framework, we are going to use the native IOC of AspNetCore, we are going to use the DI to manage our data.

We have already used the AspNetCore IOC with properties using the `[Inject]` attribute, these being managed by the system or external libraries.

## Creating the data service interface

We will therefore create a `Services` directory at the root of our site and create our interface.

```csharp title="Services/IDataService.cs"
public interface IDataService
{
	Task Add(ItemModel model);

	Task<int> Count();

	Task<List<Item>> List(int currentPage, int pageSize);
}
```

Our interface contains the methods to manage our data.

## Creating the Data Service Implementation

We will now create our class to manage our data locally.

```csharp title="Services/DataLocalService.cs"
public class DataLocalService : IDataService
{
	private readonly HttpClient _http;
	private readonly ILocalStorageService _localStorage;
	private readonly NavigationManager _navigationManager;
	private readonly IWebHostEnvironment _webHostEnvironment;

	public DataLocalService(
		ILocalStorageService localStorage,
		HttpClient http,
		IWebHostEnvironment webHostEnvironment,
		NavigationManager navigationManager)
	{
		_localStorage = localStorage;
		_http = http;
		_webHostEnvironment = webHostEnvironment;
		_navigationManager = navigationManager;
	}

	public async Task Add(ItemModel model)
	{
		// Get the current data
		var currentData = await _localStorage.GetItemAsync<List<Item>>("data");

		// Simulate the Id
		model.Id = currentData.Max(s => s.Id) + 1;

		// Add the item to the current data
		currentData.Add(new Item
		{
			Id = model.Id,
			DisplayName = model.DisplayName,
			Name = model.Name,
			RepairWith = model.RepairWith,
			EnchantCategories = model.EnchantCategories,
			MaxDurability = model.MaxDurability,
			StackSize = model.StackSize,
			CreatedDate = DateTime.Now
		});

		// Save the image
		var imagePathInfo = new DirectoryInfo($"{_webHostEnvironment.WebRootPath}/images");

		// Check if the folder "images" exist
		if (!imagePathInfo.Exists)
		{
			imagePathInfo.Create();
		}

		// Determine the image name
		var fileName = new FileInfo($"{imagePathInfo}/{model.Name}.png");

		// Write the file content
		await File.WriteAllBytesAsync(fileName.FullName, model.ImageContent);

		// Save the data
		await _localStorage.SetItemAsync("data", currentData);
	}

	public async Task<int> Count()
	{
		// Load data from the local storage
		var currentData = await _localStorage.GetItemAsync<Item[]>("data");

		// Check if data exist in the local storage
		if (currentData == null)
		{
			// this code add in the local storage the fake data
			var originalData = await _http.GetFromJsonAsync<Item[]>($"{_navigationManager.BaseUri}fake-data.json");
			await _localStorage.SetItemAsync("data", originalData);
		}
		
		return (await _localStorage.GetItemAsync<Item[]>("data")).Length;
	}

	public async Task<List<Item>> List(int currentPage, int pageSize)
	{
		// Load data from the local storage
		var currentData = await _localStorage.GetItemAsync<Item[]>("data");

		// Check if data exist in the local storage
		if (currentData == null)
		{
			// this code add in the local storage the fake data
			var originalData = await _http.GetFromJsonAsync<Item[]>($"{_navigationManager.BaseUri}fake-data.json");
			await _localStorage.SetItemAsync("data", originalData);
		}

		return (await _localStorage.GetItemAsync<Item[]>("data")).Skip((currentPage - 1) * pageSize).Take(pageSize).ToList();
	}
}
```

You will notice that we use another way to inject dependencies into this class through the constructor.
`ILocalStorageService` & `HttpClient` & `IWebHostEnvironment` & `NavigationManager` are automatically resolved by the IOC.

## Editing Pages

Let's modify our pages to take into account our new service:

```csharp title="Pages/List.razor.cs"
public partial class List
{
	private List<Item> items;

	private int totalItem;

	[Inject]
	public IDataService DataService { get; set; }

	[Inject]
	public IWebHostEnvironment WebHostEnvironment { get; set; }

	private async Task OnReadData(DataGridReadDataEventArgs<Item> e)
	{
		if (e.CancellationToken.IsCancellationRequested)
		{
			return;
		}

		if (!e.CancellationToken.IsCancellationRequested)
		{
			items = await DataService.List(e.Page, e.PageSize);
			totalItem = await DataService.Count();
		}
	}
}
```

```csharp title="Pages/Add.razor.cs"
public partial class Add
{
	/// <summary>
	/// The default enchant categories.
	/// </summary>
	private List<string> enchantCategories = new List<string>() { "armor", "armor_head", "armor_chest", "weapon", "digger", "breakable", "vanishable" };

	/// <summary>
	/// The current item model
	/// </summary>
	private ItemModel itemModel = new()
	{
		EnchantCategories = new List<string>(),
		RepairWith = new List<string>()
	};

	/// <summary>
	/// The default repair with.
	/// </summary>
	private List<string> repairWith = new List<string>() { "oak_planks", "spruce_planks", "birch_planks", "jungle_planks", "acacia_planks", "dark_oak_planks", "crimson_planks", "warped_planks" };

	[Inject]
	public IDataService DataService { get; set; }

	[Inject]
	public NavigationManager NavigationManager { get; set; }

	private async void HandleValidSubmit()
	{
		await DataService.Add(itemModel);

		NavigationManager.NavigateTo("list");
	}

	private async Task LoadImage(InputFileChangeEventArgs e)
	{
		// Set the content of the image to the model
		using (var memoryStream = new MemoryStream())
		{
			await e.File.OpenReadStream().CopyToAsync(memoryStream);
			itemModel.ImageContent = memoryStream.ToArray();
		}
	}

	private void OnEnchantCategoriesChange(string item, object checkedValue)
	{
		if ((bool)checkedValue)
		{
			if (!itemModel.EnchantCategories.Contains(item))
			{
				itemModel.EnchantCategories.Add(item);
			}

			return;
		}

		if (itemModel.EnchantCategories.Contains(item))
		{
			itemModel.EnchantCategories.Remove(item);
		}
	}

	private void OnRepairWithChange(string item, object checkedValue)
	{
		if ((bool)checkedValue)
		{
			if (!itemModel.RepairWith.Contains(item))
			{
				itemModel.RepairWith.Add(item);
			}

			return;
		}

		if (itemModel.RepairWith.Contains(item))
		{
			itemModel.RepairWith.Remove(item);
		}
	}
}
```

## Register the data service

Now we have to define in the IOC of our application the resolution of our interface / class.

Open the `Program.cs` file and add the following line:

```csharp title="Program.cs"
...

builder.Services.AddScoped<IDataService, DataLocalService>();

...
```

Later we will implement data management through an API, it will simply be enough to create a new class `Services/DataApiService.cs` implementing the interface `IDataService` with API calls and modify the IOC with this new service.
