Open jthompsonnait opened 1 year ago
@using MatBlazor
<script src="_content/MatBlazor/dist/matBlazor.js"></script>
<link href="_content/MatBlazor/dist/matBlazor.css" rel="stylesheet" />
<component type="typeof(App)" render-mode="Server" />
using MatBlazor;
builder.Services.AddMatBlazor();
// creating an object in LINQ Pad
PlaylistTracks playlistTrack = null;
// creating an object in Visual Studio
PlaylistTrack playlistTrack = null;
// check that the incoming data exists
trackExist = Tracks
.Where(x => x.TrackId == trackId)
....
// this now becomes the following trackExist = _playlistManagementContext.Tracks .Where(x => x.TrackId == trackId) ....
// saving data to the database now becomes the following _playlistManagementContext.SaveChanges();
## Error Handling
- In LINQ Pad, when you run a method and an error(s) is thrown from the error list, the process terminates after showing the error message(s). However, on a web server, after the errors(s) are thrown, the LINQ transactions is still held in memory with the assumption that we are going to handle it. If we try and do any further LINQ transactions, we will get the following message:
<br>
_The instance of entity type 'PlaylistTrack' cannot be tracked because another instance with the same key value for {'PlaylistId', 'TrackId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values._
<br>
Because of this, we need to clear the tracking of any notable changes the system is referencing.
```csharp
// we need to clear the "track changes"; otherwise, we leave our entity system in flux
_playlistManagementContext.ChangeTracker.Clear();
@page "/SamplePages/NavigationFrom"
<h3>Navigation From</h3>
<TableTemplate Items="PaginatorTrackSelection.Results" Context="TrackSelectionView">
<TableHeader>
<th></th>
<th class="SortField @GetSortColumn("SongName")" @onclick="@( () => Sort("SongName"))">Song</th>
<th class="SortField @GetSortColumn("AlbumTitle")" @onclick="@( () => Sort("AlbumTitle"))">Album</th>
<th class="SortField @GetSortColumn("ArtistName")" @onclick="@( () => Sort("ArtistName"))">Artist</th>
<th class="SortField @GetSortColumn("Milliseconds")" @onclick="@( () => Sort("Milliseconds"))">Length</th>
<th>Price</th>
</TableHeader>
<RowTemplate>
<td>
<button class="btn-success" @onclick="(() => Edit(TrackSelectionView.TrackId))">Edit</button>
</td>
<td>@TrackSelectionView.SongName</td>
<td>@TrackSelectionView.AlbumTitle</td>
<td>@TrackSelectionView.ArtistName</td>
<td>@TrackSelectionView.Length</td>
<td>@TrackSelectionView.Price</td>
</RowTemplate>
</TableTemplate>
<div align="center">
<BlazorPager CurrentPage="PaginatorTrackSelection.CurrentPage"
PageCount="PaginatorTrackSelection.PageCount"
VisiblePages="3"
ShowFirstLast="true"
ShowPageNumbers="true"
FirstText="«"
LastText="»"
NextText="›"
PreviousText="‹"
OnPageChanged="(async e => { CurrentPage = e; await FetchArtistOrAlbumTracks();})">
</BlazorPager>
</div>
public partial class NavigationFrom
{
[Inject]
protected PlaylistTrackService? PlaylistTrackService { get; set; }
[Inject]
protected NavigationManager? NavigationManager { get; set; }
#region Fields
private string searchPattern { get; set; } = "Deep";
private string searchType { get; set; } = "Artist";
#endregion
#region Paginator
private const int PAGE_SIZE = 10;
protected string SortField { get; set; } = "Owner";
protected string Direction { get; set; } = "desc";
protected int CurrentPage { get; set; } = 1;
protected PagedResult<TrackSelectionView> PaginatorTrackSelection { get; set; } = new();
private async void Sort(string column)
{
Direction = SortField == column ? Direction == "asc" ? "desc" : "asc" : "asc";
SortField = column;
if (!string.IsNullOrWhiteSpace(searchPattern))
{
await FetchArtistOrAlbumTracks();
}
}
private string GetSortColumn(string x)
{
return x == SortField ? Direction == "desc" ? "desc" : "asc" : "";
}
#endregion
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
// load data on page load
await FetchArtistOrAlbumTracks();
await InvokeAsync(StateHasChanged);
}
private async Task FetchArtistOrAlbumTracks()
{
PaginatorTrackSelection = await PlaylistTrackService.FetchArtistOrAlbumTracks(searchType, searchPattern, CurrentPage, PAGE_SIZE, SortField, Direction); //Note that the following line is necessary because otherwise
await InvokeAsync(StateHasChanged);
}
// pass the track Id to the "NavigationTo" page
protected void Edit(int trackId)
{
NavigationManager.NavigateTo($"/SamplePages/NavigationTo/{trackId}");
}
}
@page "/SamplePages/NavigationTo/{EditTrackId}"
<h3>Navigation To</h3>
<div>
<label>Track ID:</label>
<label>@trackSelection.TrackId</label>
<p>
<label>Album Title:</label>
<label>@trackSelection.AlbumTitle</label>
</p>
<p>
<label>Artist Name:</label>
<label>@trackSelection.ArtistName</label>
</p>
<p>
<label>Song Name:</label>
<label>@trackSelection.SongName</label>
</p>
</div>
public partial class NavigationTo
{
[Inject] protected PlaylistTrackService? PlaylistTrackService { get; set; }
#region Fields
private TrackSelectionView trackSelection { get; set; } = new();
#endregion
#region Properties
[Parameter]
public string EditTrackId { get; set; }
#endregion
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
EditTrackId = EditTrackId;
// check to see if we are navigating using a valid track Id
// or are we going directly to the page from the menu
if (!string.IsNullOrWhiteSpace(EditTrackId))
{
trackSelection = await PlaylistTrackService.GetTrack(int.Parse(EditTrackId));
}
await InvokeAsync(StateHasChanged);
}
}
<NavLink class="nav-link" href="/SamplePages/NavigationFrom">
<span>Navigation From</span>
</NavLink>
<NavLink class="nav-link" href="/SamplePages/NavigationTo">
<span>Navigation To</span>
</NavLink>
When displaying an AggregateException within a LINQ Pad editor, you would run the following code to show the exception.
catch (AggregateException ex)
{
foreach (var error in ex.InnerExceptions)
{
error.Message.Dump();
}
}
// placeholder for feedback messages
private string feedBackMessage { get; set; }
// placeholder for error messages
private string errorMessage { get; set; }
//a get property that returns the result of the lamda action
private bool hasError => !string.IsNullOrWhiteSpace(errorMessage);
private bool hasFeedBack => !string.IsNullOrWhiteSpace(feedBackMessage);
//used to display any collection of errors on web page
//whether the errors are generated locally OR come form the class library
// service methods
private List<string> errorDetails { get; set; } = new();
try
{
// reset the error detail list
errorDetails.Clear();
// reset error message to an empty string
errorMessage = string.Empty;
// reset feedback message to an empty string
feedBackMessage = string.Empty;
// call our service methods from the BLL
Service.ServiceMethod();
// update the feedback with a successful message
feedBackMessage = "XXX was successfully XXXX";
}
catch (ArgumentNullException ex)
{
errorMessage = GetInnerException(ex).Message;
}
catch (ArgumentException ex)
{
errorMessage = GetInnerException(ex).Message;
}
catch (AggregateException ex)
{
// having collected a number of errors
// each error should be places into a separate line
errorMessage = "Unable to add a xxxxx";
foreach (var error in ex.InnerExceptions)
{
errorDetails.Add(error.Message);
}
}
catch (Exception ex)
{
errorMessage = GetInnerException(ex).Message;
}
@if (@hasFeedBack)
{
<blockquote class="success">@feedBackMessage</blockquote>
}
@if (hasError)
{
<blockquote class="error">
@errorMessage
<ul>
@foreach (var detail in errorDetails)
{
<li>@detail</li>
}
</ul>
</blockquote>
}
Two-Way Binding with Input elements
Two-Way Binding with MatTextField
Methods will auto-fire if you include the parameters brackets
Bad Code
Good Code