You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Blazor/Documentation/docusaurus/docs/di-ioc/create-data-service.md

6.5 KiB

sidebar_position title
3 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.

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.

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()
	{
		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:

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();
		}
	}
}
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:

...

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.