├── .gitignore ├── LICENSE ├── README.md ├── ch1 └── REST_API.md ├── ch2 ├── App.razor ├── Counter.razor ├── CustomForm.razor ├── CustomInput.razor ├── Headline.razor ├── LikeButton.razor ├── List.razor ├── PageDirective.razor ├── PageHeader.razor ├── Parent.razor ├── Posts │ └── AllPosts.razor ├── Product.razor ├── _Imports.razor └── demo │ └── MediaLibrary │ ├── MediaLibrary.sln │ └── MediaLibrary │ ├── Client │ ├── App.razor │ ├── MediaLibrary.Client.csproj │ ├── Pages │ │ └── Index.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── _Imports.razor │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── open-iconic │ │ │ ├── FONT-LICENSE │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── font │ │ │ ├── css │ │ │ └── open-iconic-bootstrap.min.css │ │ │ └── fonts │ │ │ ├── open-iconic.eot │ │ │ ├── open-iconic.otf │ │ │ ├── open-iconic.svg │ │ │ ├── open-iconic.ttf │ │ │ └── open-iconic.woff │ │ ├── favicon.ico │ │ ├── icon-192.png │ │ └── index.html │ ├── Server │ ├── MediaLibrary.Server.csproj │ ├── Pages │ │ ├── Error.cshtml │ │ └── Error.cshtml.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json │ └── Shared │ └── MediaLibrary.Shared.csproj ├── ch3 └── demo │ └── MediaLibrary │ ├── MediaLibrary.sln │ └── MediaLibrary │ ├── Client │ ├── App.razor │ ├── MediaLibrary.Client.csproj │ ├── Pages │ │ └── Index.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── _Imports.razor │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── open-iconic │ │ │ ├── FONT-LICENSE │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── font │ │ │ ├── css │ │ │ └── open-iconic-bootstrap.min.css │ │ │ └── fonts │ │ │ ├── open-iconic.eot │ │ │ ├── open-iconic.otf │ │ │ ├── open-iconic.svg │ │ │ ├── open-iconic.ttf │ │ │ └── open-iconic.woff │ │ ├── favicon.ico │ │ ├── icon-192.png │ │ └── index.html │ ├── Server │ ├── Data │ │ ├── BaseEntity.cs │ │ ├── MediaLibraryDbContext.cs │ │ ├── Movie.cs │ │ ├── MovieActor.cs │ │ ├── MovieCategory.cs │ │ └── Person.cs │ ├── MapperProfile.cs │ ├── MediaLibrary.Server.csproj │ ├── Migrations │ │ ├── 20220512144428_InitialMigration.Designer.cs │ │ ├── 20220512144428_InitialMigration.cs │ │ ├── 20220519062109_Entities.Designer.cs │ │ ├── 20220519062109_Entities.cs │ │ └── MediaLibraryDbContextModelSnapshot.cs │ ├── Pages │ │ ├── Error.cshtml │ │ └── Error.cshtml.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Services │ │ ├── BaseService.cs │ │ ├── MovieService.cs │ │ └── PersonService.cs │ └── appsettings.json │ └── Shared │ ├── CategoryType.cs │ ├── MediaLibrary.Shared.csproj │ └── Model │ ├── IModel.cs │ ├── MovieModel.cs │ └── PersonModel.cs ├── ch4 └── demo │ └── MediaLibrary │ ├── MediaLibrary.sln │ └── MediaLibrary │ ├── Client │ ├── App.razor │ ├── MediaLibrary.Client.csproj │ ├── Pages │ │ ├── Index.razor │ │ ├── MovieDetail.razor │ │ ├── MovieList.razor │ │ ├── PersonDetail.razor │ │ └── PersonList.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Shared │ │ ├── DataForm.razor │ │ ├── DataForm.razor.cs │ │ ├── DataView.razor │ │ ├── DataView.razor.cs │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── Model │ │ │ ├── Table.cs │ │ │ ├── TableCell.cs │ │ │ ├── TableColumn.cs │ │ │ └── TableRow.cs │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── _Imports.razor │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── open-iconic │ │ │ ├── FONT-LICENSE │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── font │ │ │ ├── css │ │ │ └── open-iconic-bootstrap.min.css │ │ │ └── fonts │ │ │ ├── open-iconic.eot │ │ │ ├── open-iconic.otf │ │ │ ├── open-iconic.svg │ │ │ ├── open-iconic.ttf │ │ │ └── open-iconic.woff │ │ ├── favicon.ico │ │ ├── icon-192.png │ │ └── index.html │ ├── Server │ ├── Controllers │ │ ├── BaseController.cs │ │ ├── MovieController.cs │ │ └── PersonController.cs │ ├── Data │ │ ├── BaseEntity.cs │ │ ├── MediaLibraryDbContext.cs │ │ ├── Movie.cs │ │ ├── MovieActor.cs │ │ ├── MovieCategory.cs │ │ └── Person.cs │ ├── MapperProfile.cs │ ├── MediaLibrary.Server.csproj │ ├── Migrations │ │ ├── 20220512144428_InitialMigration.Designer.cs │ │ ├── 20220512144428_InitialMigration.cs │ │ ├── 20220519062109_Entities.Designer.cs │ │ ├── 20220519062109_Entities.cs │ │ └── MediaLibraryDbContextModelSnapshot.cs │ ├── Pages │ │ ├── Error.cshtml │ │ └── Error.cshtml.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Services │ │ ├── BaseService.cs │ │ ├── MovieService.cs │ │ └── PersonService.cs │ └── appsettings.json │ └── Shared │ ├── CategoryType.cs │ ├── MediaLibrary.Shared.csproj │ └── Model │ ├── IModel.cs │ ├── MovieModel.cs │ └── PersonModel.cs ├── ch5 ├── 01_demo_implementation │ └── MediaLibrary │ │ ├── MediaLibrary.Contracts │ │ ├── Clients │ │ │ ├── IContractClient.cs │ │ │ └── PersonContract.cs │ │ ├── MediaLibrary.Contracts.csproj │ │ ├── person.proto │ │ └── shared.proto │ │ ├── MediaLibrary.sln │ │ └── MediaLibrary │ │ ├── Client │ │ ├── App.razor │ │ ├── MediaLibrary.Client.csproj │ │ ├── Pages │ │ │ ├── Index.razor │ │ │ ├── MovieDetail.razor │ │ │ ├── MovieList.razor │ │ │ ├── PersonDetail.razor │ │ │ └── PersonList.razor │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Shared │ │ │ ├── DataForm.razor │ │ │ ├── DataForm.razor.cs │ │ │ ├── DataView.razor │ │ │ ├── DataView.razor.cs │ │ │ ├── MainLayout.razor │ │ │ ├── MainLayout.razor.css │ │ │ ├── Model │ │ │ │ ├── Table.cs │ │ │ │ ├── TableCell.cs │ │ │ │ ├── TableColumn.cs │ │ │ │ └── TableRow.cs │ │ │ ├── NavMenu.razor │ │ │ └── NavMenu.razor.css │ │ ├── _Imports.razor │ │ └── wwwroot │ │ │ ├── css │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── open-iconic │ │ │ │ ├── FONT-LICENSE │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ ├── favicon.ico │ │ │ ├── icon-192.png │ │ │ └── index.html │ │ ├── Server │ │ ├── Contracts │ │ │ ├── IContractService.cs │ │ │ └── PersonContractService.cs │ │ ├── Controllers │ │ │ ├── BaseController.cs │ │ │ ├── MovieController.cs │ │ │ └── PersonController.cs │ │ ├── Data │ │ │ ├── BaseEntity.cs │ │ │ ├── MediaLibraryDbContext.cs │ │ │ ├── Movie.cs │ │ │ ├── MovieActor.cs │ │ │ ├── MovieCategory.cs │ │ │ └── Person.cs │ │ ├── MapperProfile.cs │ │ ├── MediaLibrary.Server.csproj │ │ ├── Migrations │ │ │ ├── 20220512144428_InitialMigration.Designer.cs │ │ │ ├── 20220512144428_InitialMigration.cs │ │ │ ├── 20220519062109_Entities.Designer.cs │ │ │ ├── 20220519062109_Entities.cs │ │ │ └── MediaLibraryDbContextModelSnapshot.cs │ │ ├── Pages │ │ │ ├── Error.cshtml │ │ │ └── Error.cshtml.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Services │ │ │ ├── BaseService.cs │ │ │ ├── MovieService.cs │ │ │ └── PersonService.cs │ │ └── appsettings.json │ │ └── Shared │ │ ├── CategoryType.cs │ │ ├── MediaLibrary.Shared.csproj │ │ ├── Model │ │ ├── IModel.cs │ │ ├── MovieModel.cs │ │ └── PersonModel.cs │ │ └── SharedMapperProfile.cs └── 02_demo_blazor │ └── MediaLibrary │ ├── MediaLibrary.Contracts │ ├── Clients │ │ ├── IContractClient.cs │ │ ├── MovieContract.cs │ │ └── PersonContract.cs │ ├── MediaLibrary.Contracts.csproj │ ├── movie.proto │ ├── person.proto │ └── shared.proto │ ├── MediaLibrary.sln │ └── MediaLibrary │ ├── Client │ ├── App.razor │ ├── MediaLibrary.Client.csproj │ ├── Pages │ │ ├── Index.razor │ │ ├── MovieDetail.razor │ │ ├── MovieList.razor │ │ ├── PersonDetail.razor │ │ └── PersonList.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Shared │ │ ├── DataForm.razor │ │ ├── DataForm.razor.cs │ │ ├── DataView.razor │ │ ├── DataView.razor.cs │ │ ├── GrpcDataForm.razor │ │ ├── GrpcDataForm.razor.cs │ │ ├── GrpcDataView.razor │ │ ├── GrpcDataView.razor.cs │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── Model │ │ │ ├── Table.cs │ │ │ ├── TableCell.cs │ │ │ ├── TableColumn.cs │ │ │ └── TableRow.cs │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── _Imports.razor │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── open-iconic │ │ │ ├── FONT-LICENSE │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── font │ │ │ ├── css │ │ │ └── open-iconic-bootstrap.min.css │ │ │ └── fonts │ │ │ ├── open-iconic.eot │ │ │ ├── open-iconic.otf │ │ │ ├── open-iconic.svg │ │ │ ├── open-iconic.ttf │ │ │ └── open-iconic.woff │ │ ├── favicon.ico │ │ ├── icon-192.png │ │ └── index.html │ ├── Server │ ├── Contracts │ │ ├── IContractService.cs │ │ ├── MovieContractService.cs │ │ └── PersonContractService.cs │ ├── Controllers │ │ ├── BaseController.cs │ │ ├── MovieController.cs │ │ └── PersonController.cs │ ├── Data │ │ ├── BaseEntity.cs │ │ ├── MediaLibraryDbContext.cs │ │ ├── Movie.cs │ │ ├── MovieActor.cs │ │ ├── MovieCategory.cs │ │ └── Person.cs │ ├── MapperProfile.cs │ ├── MediaLibrary.Server.csproj │ ├── Migrations │ │ ├── 20220512144428_InitialMigration.Designer.cs │ │ ├── 20220512144428_InitialMigration.cs │ │ ├── 20220519062109_Entities.Designer.cs │ │ ├── 20220519062109_Entities.cs │ │ └── MediaLibraryDbContextModelSnapshot.cs │ ├── Pages │ │ ├── Error.cshtml │ │ └── Error.cshtml.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Services │ │ ├── BaseService.cs │ │ ├── MovieService.cs │ │ └── PersonService.cs │ └── appsettings.json │ └── Shared │ ├── CategoryType.cs │ ├── MediaLibrary.Shared.csproj │ ├── Model │ ├── IModel.cs │ ├── MovieModel.cs │ └── PersonModel.cs │ └── SharedMapperProfile.cs └── ch6 └── 01_demo └── MediaLibrary ├── MediaLibrary.Contracts ├── Clients │ ├── IContractClient.cs │ ├── MovieContract.cs │ └── PersonContract.cs ├── MediaLibrary.Contracts.csproj ├── movie.proto ├── person.proto └── shared.proto ├── MediaLibrary.Generators ├── ClassData.cs ├── CustomGenerator.cs ├── MediaLibrary.Generators.csproj └── SyntaxReceiver.cs ├── MediaLibrary.sln └── MediaLibrary ├── Client ├── App.razor ├── MediaLibrary.Client.csproj ├── Pages │ ├── Index.razor │ ├── MovieDetail.razor │ ├── MovieList.razor │ ├── PersonDetail.razor │ └── PersonList.razor ├── Program.cs ├── Properties │ └── launchSettings.json ├── Shared │ ├── DataForm.razor │ ├── DataForm.razor.cs │ ├── DataView.razor │ ├── DataView.razor.cs │ ├── GrpcDataForm.razor │ ├── GrpcDataForm.razor.cs │ ├── GrpcDataView.razor │ ├── GrpcDataView.razor.cs │ ├── MainLayout.razor │ ├── MainLayout.razor.css │ ├── Model │ │ ├── Table.cs │ │ ├── TableCell.cs │ │ ├── TableColumn.cs │ │ └── TableRow.cs │ ├── NavMenu.razor │ └── NavMenu.razor.css ├── _Imports.razor └── wwwroot │ ├── css │ ├── app.css │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ └── open-iconic │ │ ├── FONT-LICENSE │ │ ├── ICON-LICENSE │ │ ├── README.md │ │ └── font │ │ ├── css │ │ └── open-iconic-bootstrap.min.css │ │ └── fonts │ │ ├── open-iconic.eot │ │ ├── open-iconic.otf │ │ ├── open-iconic.svg │ │ ├── open-iconic.ttf │ │ └── open-iconic.woff │ ├── favicon.ico │ ├── icon-192.png │ └── index.html ├── Server ├── Contracts │ ├── IContractService.cs │ ├── MovieContractService.cs │ └── PersonContractService.cs ├── Controllers │ ├── BaseController.cs │ ├── MovieController.cs │ └── PersonController.cs ├── Data │ ├── BaseEntity.cs │ ├── MediaLibraryDbContext.cs │ ├── Movie.cs │ ├── MovieActor.cs │ ├── MovieCategory.cs │ └── Person.cs ├── MapperProfile.cs ├── MediaLibrary.Server.csproj ├── Migrations │ ├── 20220512144428_InitialMigration.Designer.cs │ ├── 20220512144428_InitialMigration.cs │ ├── 20220519062109_Entities.Designer.cs │ ├── 20220519062109_Entities.cs │ └── MediaLibraryDbContextModelSnapshot.cs ├── Pages │ ├── Error.cshtml │ └── Error.cshtml.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Services │ ├── BaseService.cs │ ├── MovieService.cs │ └── PersonService.cs ├── UseCustomGeneratorAttribute.cs └── appsettings.json └── Shared ├── CategoryType.cs ├── MediaLibrary.Shared.csproj ├── Model ├── IModel.cs ├── MovieModel.cs └── PersonModel.cs └── SharedMapperProfile.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ch1/REST_API.md: -------------------------------------------------------------------------------- 1 | # REST API examples 2 | ## POST method 3 | ### Request: 4 | ``` 5 | Method: POST 6 | URL: https://api.example.org/dotnetversions 7 | Data: { "name": ".NET 6.0", "version": 6.0 } 8 | ``` 9 | ### Response: 10 | ``` 11 | Status: 201 Created 12 | Location: https://api.example.org/dotnetversions/18 13 | Data: { "id": 18, "name": ".NET 6.0", "version": 6.0 } 14 | ``` 15 | ## GET method 16 | ### Request: 17 | ``` 18 | Method: GET 19 | URL: https://api.example.org/dotnetversions/10 20 | ``` 21 | ### Response: 22 | ``` 23 | Status: 200 OK 24 | Data: { "id": 10, "name": ".NET 5.0", "version": 5.0 } 25 | ``` 26 | ### Request: 27 | ``` 28 | Method: GET 29 | URL: https://api.example.org/dotnetversions?min-version=4&max-version=5 30 | ``` 31 | ### Response: 32 | ``` 33 | Status: 200 OK 34 | Data: [ 35 | { "id": 7, "name": "net framework 4", "version": 4.0 }, 36 | { "id": 8, "name": "net framework 4.7.2", "version": 4.7 }, 37 | { "id": 9, "name": "net framework 4.8", "version": 4.8 }, 38 | { "id": 10, "name": ".NET 5.0", "version": 5.0 } 39 | ] 40 | ``` 41 | ## PUT method 42 | ### Request: 43 | ``` 44 | Method: PUT 45 | URL: https://api.example.org/dotnetversions/9 46 | Data: { "id": 9, "name": "net framework 4.8.2", "version": 4.8 }, 47 | ``` 48 | ### Response: 49 | ``` 50 | Status: 204 No Content 51 | ``` 52 | ## DELETE method 53 | ### Request: 54 | ``` 55 | Method: DELETE 56 | URL: https://api.example.org/dotnetversions/1 57 | ``` 58 | ### Response: 59 | ``` 60 | Status: 204 No Content 61 | ``` 62 | -------------------------------------------------------------------------------- /ch2/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch2/Counter.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 | 3 |

Number of clicks: @count

4 | 7 | 8 | @code { 9 | int count = 0; 10 | } 11 | -------------------------------------------------------------------------------- /ch2/CustomForm.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 | 3 | 4 | 5 | @code { 6 | private string message = ""; 7 | 8 | // Here is other code 9 | } 10 | -------------------------------------------------------------------------------- /ch2/CustomInput.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | @code { 4 | [Parameter] 5 | public string BindingValue 6 | { 7 | get => _value; 8 | set 9 | { 10 | if (_value == value ) return; 11 | _value = value; 12 | BindingValueChanged.InvokeAsync(value); 13 | } 14 | } 15 | 16 | [Parameter] 17 | public EventCallback BindingValueChanged { get; set; } 18 | } -------------------------------------------------------------------------------- /ch2/Headline.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components.Structures 2 |

Page @Title

3 | 4 | @code { 5 | [Parameter] 6 | public string Title { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch2/LikeButton.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 | 3 | 6 | 7 | @code { 8 | [Parameter] 9 | public EvenCallback OnClickEvent { get; set; } 10 | 11 | private async Task OnClickHandler() 12 | { 13 | await OnClickEvent.InvokeAsync("black") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ch2/List.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 | 3 |
4 | @foreach(var product in products) 5 | { 6 | 7 | 8 | Demo image 9 | 10 | Product name 11 | Description & Price 12 | 13 | } 14 |
15 | 16 | @code { 17 | private int[] products = new[] { 1, 2, 3}; 18 | } -------------------------------------------------------------------------------- /ch2/PageDirective.razor: -------------------------------------------------------------------------------- 1 | @page "/author" 2 | @page "/author/{Name}" 3 |

Author @authorName

4 | 5 | @code { 6 | private string authorName; 7 | 8 | [Parameter] 9 | public string Name { get; set; } 10 | 11 | protected override void OnInitialized() 12 | { 13 | authorName = Name ?? "Not set"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ch2/PageHeader.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components.Structures 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /ch2/Parent.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 |
3 | 4 |

Selected color is @color

5 | 6 | @code { 7 | private string color = "white"; 8 | private void OnClickAction(string args) 9 | { 10 | color = args; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ch2/Posts/AllPosts.razor: -------------------------------------------------------------------------------- 1 | @page "/posts/{*pageRoute}" 2 | @code { 3 | [Parameter] 4 | public string? PageRoute { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /ch2/Product.razor: -------------------------------------------------------------------------------- 1 | @namespace SampleApp.Components 2 | 3 |
4 |
@Image
5 |
@Name
6 |
@Info
7 |
8 | 9 | @code { 10 | [Parameter] 11 | public RenderFragment Image { get; set; } 12 | 13 | [Parameter] 14 | public RenderFragment Name { get; set; } 15 | 16 | [Parameter] 17 | public RenderFragment Info { get; set; } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /ch2/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using SampleApp.Components 2 | @using SampleApp.Components.Structures -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Client; 2 | using Microsoft.AspNetCore.Components.Web; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | builder.RootComponents.Add("#app"); 7 | builder.RootComponents.Add("head::after"); 8 | 9 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 10 | 11 | await builder.Build().RunAsync(); 12 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared 11 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.ResponseCompression; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | // Add services to the container. 6 | 7 | builder.Services.AddControllersWithViews(); 8 | builder.Services.AddRazorPages(); 9 | 10 | var app = builder.Build(); 11 | 12 | // Configure the HTTP request pipeline. 13 | if (app.Environment.IsDevelopment()) 14 | { 15 | app.UseWebAssemblyDebugging(); 16 | } 17 | else 18 | { 19 | app.UseExceptionHandler("/Error"); 20 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 21 | app.UseHsts(); 22 | } 23 | 24 | app.UseHttpsRedirection(); 25 | 26 | app.UseBlazorFrameworkFiles(); 27 | app.UseStaticFiles(); 28 | 29 | app.UseRouting(); 30 | 31 | 32 | app.MapRazorPages(); 33 | app.MapControllers(); 34 | app.MapFallbackToFile("index.html"); 35 | 36 | app.Run(); 37 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch2/demo/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Client; 2 | using Microsoft.AspNetCore.Components.Web; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | builder.RootComponents.Add("#app"); 7 | builder.RootComponents.Add("head::after"); 8 | 9 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 10 | 11 | await builder.Build().RunAsync(); 12 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 18 |
19 | 20 | @code { 21 | private bool collapseNavMenu = true; 22 | 23 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 24 | 25 | private void ToggleNavMenu() 26 | { 27 | collapseNavMenu = !collapseNavMenu; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared 11 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class BaseEntity 5 | { 6 | [Key] 7 | public int Id { get; set; } 8 | public string Name { get; set; } = string.Empty; 9 | } 10 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/MediaLibraryDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class MediaLibraryDbContext : DbContext 5 | { 6 | public MediaLibraryDbContext() { } 7 | 8 | public MediaLibraryDbContext(DbContextOptions options) : base(options) { } 9 | 10 | #nullable disable 11 | public DbSet Persons { get; set; } 12 | public DbSet Movies { get; set; } 13 | #nullable enable 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.Entity(b => 18 | { 19 | b.Property(x => x.Name).IsRequired(); 20 | b.Navigation(x => x.Movies).AutoInclude(); 21 | }); 22 | 23 | modelBuilder.Entity(b => 24 | { 25 | b.Property(x => x.Name).IsRequired(); 26 | b.HasOne(x => x.Director).WithMany().HasForeignKey(x => x.DirectorId).OnDelete(DeleteBehavior.Restrict); 27 | b.HasOne(x => x.MusicComposer).WithMany().HasForeignKey(x => x.MusicComposerId).OnDelete(DeleteBehavior.Restrict); 28 | b.Navigation(x => x.Director).AutoInclude(); 29 | b.Navigation(x => x.MusicComposer).AutoInclude(); 30 | b.Navigation(x => x.Actors).AutoInclude(); 31 | }); 32 | 33 | modelBuilder.Entity(b => 34 | { 35 | b.HasKey(x => new { x.Category, x.MovieId }); 36 | }); 37 | 38 | modelBuilder.Entity(b => 39 | { 40 | b.HasKey(x => new { x.MovieId, x.PersonId }); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/Movie.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Movie : BaseEntity 4 | { 5 | public List Categories { get; set; } = new List(); 6 | public int Year { get; set; } 7 | public string? Description { get; set; } 8 | public Person? Director { get; set; } 9 | public int? DirectorId { get; set; } 10 | public Person? MusicComposer { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public List Actors { get; set; } = new List(); 13 | } 14 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/MovieActor.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class MovieActor 4 | { 5 | public int MovieId { get; set; } 6 | public Movie Movie { get; set; } = null!; 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } = null!; 9 | } 10 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/MovieCategory.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Shared; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | 5 | public class MovieCategory 6 | { 7 | public int MovieId { get; set; } 8 | public Movie Movie { get; set; } = null!; 9 | public CategoryType Category { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Data/Person.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Person : BaseEntity 4 | { 5 | public DateTime BirthDay { get; set; } 6 | public string BirthPlace { get; set; } = string.Empty; 7 | public string Biography { get; set; } = string.Empty; 8 | public List Movies { get; set; } = new List(); 9 | } 10 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Migrations/20220512144428_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace MediaLibrary.Server.Migrations 7 | { 8 | public partial class InitialMigration : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Persons", 14 | columns: table => new 15 | { 16 | Id = table.Column(type: "int", nullable: false) 17 | .Annotation("SqlServer:Identity", "1, 1"), 18 | BirthDay = table.Column(type: "datetime2", nullable: false), 19 | BirthPlace = table.Column(type: "nvarchar(max)", nullable: false), 20 | Biography = table.Column(type: "nvarchar(max)", nullable: false), 21 | Name = table.Column(type: "nvarchar(max)", nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Persons", x => x.Id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "Persons"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server; 2 | using MediaLibrary.Server.Data; 3 | using MediaLibrary.Server.Services; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | builder.Services.AddDbContext(options => 8 | { 9 | options.UseSqlServer(builder.Configuration.GetConnectionString("MediaLibrary")); 10 | 11 | #if DEBUG 12 | options.EnableDetailedErrors(); 13 | options.EnableSensitiveDataLogging(); 14 | #endif 15 | }); 16 | 17 | // Add services to the container. 18 | 19 | builder.Services.AddControllersWithViews(); 20 | builder.Services.AddRazorPages(); 21 | builder.Services.AddAutoMapper(typeof(MapperProfile)); 22 | builder.Services.AddTransient(); 23 | builder.Services.AddTransient(); 24 | 25 | var app = builder.Build(); 26 | 27 | // Configure the HTTP request pipeline. 28 | if (app.Environment.IsDevelopment()) 29 | { 30 | app.UseWebAssemblyDebugging(); 31 | } 32 | else 33 | { 34 | app.UseExceptionHandler("/Error"); 35 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 36 | app.UseHsts(); 37 | } 38 | 39 | app.UseHttpsRedirection(); 40 | 41 | app.UseBlazorFrameworkFiles(); 42 | app.UseStaticFiles(); 43 | 44 | app.UseRouting(); 45 | 46 | 47 | app.MapRazorPages(); 48 | app.MapControllers(); 49 | app.MapFallbackToFile("index.html"); 50 | 51 | app.Run(); 52 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Services/MovieService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | 6 | public class MovieService : BaseService 7 | { 8 | public MovieService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/Services/PersonService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | public class PersonService : BaseService 6 | { 7 | public PersonService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Shared/CategoryType.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared; 2 | public enum CategoryType 3 | { 4 | Action = 0, 5 | Comedy = 1, 6 | Drama = 2, 7 | Fantasy = 3, 8 | SciFi = 4, 9 | Horror = 5, 10 | Mystery = 6, 11 | Romance = 7, 12 | Thriller = 8, 13 | Western = 9 14 | } 15 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Shared/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | string Name { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Shared/Model/MovieModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public class MovieModel : IModel 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = string.Empty; 7 | public List Categories { get; set; } = new List(); 8 | public int Year { get; set; } 9 | public string? Description { get; set; } 10 | public int? DirectorId { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public int[] ActorIds { get; set; } = Array.Empty(); 13 | } -------------------------------------------------------------------------------- /ch3/demo/MediaLibrary/MediaLibrary/Shared/Model/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Shared.Model; 4 | 5 | public class PersonModel : IModel 6 | { 7 | public int Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } = string.Empty; 11 | public DateTime BirthDay { get; set; } 12 | public string BirthPlace { get; set; } = string.Empty; 13 | public string Biography { get; set; } = string.Empty; 14 | public int[] MoviesIds { get; set; } = Array.Empty(); 15 | } 16 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Pages/MovieDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/movies/{Id:int}" 2 | 3 |

4 |

5 |

6 |

7 |

8 |
9 | 10 | @code { 11 | [Parameter] 12 | public int Id { get; set; } 13 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Pages/MovieList.razor: -------------------------------------------------------------------------------- 1 | @page "/movies" 2 | 3 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Pages/PersonDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/persons/{Id:int}" 2 | 3 |

4 |

5 |

6 |

7 |
8 | 9 | @code { 10 | [Parameter] 11 | public int Id { get; set; } 12 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Pages/PersonList.razor: -------------------------------------------------------------------------------- 1 | @page "/persons" 2 | 3 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Client; 2 | using Microsoft.AspNetCore.Components.Web; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | builder.RootComponents.Add("#app"); 7 | builder.RootComponents.Add("head::after"); 8 | 9 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 10 | 11 | await builder.Build().RunAsync(); 12 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/DataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @attribute [CascadingTypeParameter(nameof(TModel))] 3 | 4 | 5 | 6 | 7 | @ChildContent(Model) 8 | 9 | 10 |
11 | 12 | 13 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 14 | { 15 |
@_errorMessage
16 | } 17 |
-------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/DataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @using Microsoft.AspNetCore.Components.Web.Virtualization 3 | New 4 | 5 | 6 | 7 | @foreach (var column in Data.Columns) 8 | { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach (var data in item.Values) 18 | { 19 | 29 | } 30 | 31 | 36 | 37 | 38 | 39 |
@column.Name
20 | @if (data.Value is null) 21 | { 22 | --- 23 | } 24 | else 25 | { 26 | @data.Value 27 | } 28 | 32 |
33 | Edit 34 |
35 |
-------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/DataView.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using System.Net.Http.Json; 3 | using MediaLibrary.Client.Shared.Model; 4 | 5 | namespace MediaLibrary.Client.Shared 6 | { 7 | public partial class DataView 8 | where TItem : MediaLibrary.Shared.Model.IModel, new() 9 | { 10 | [Inject] 11 | public NavigationManager Navigation { get; set; } = null!; 12 | 13 | [Inject] 14 | public HttpClient Http { get; set; } = null!; 15 | 16 | [Parameter] 17 | [EditorRequired] 18 | public string ApiPath { get; set; } = string.Empty; 19 | 20 | public Table Data { get; set; } = new Table(); 21 | 22 | protected override async Task OnInitializedAsync() 23 | { 24 | var type = typeof(TItem); 25 | Data.Columns = type.GetProperties().Select(x => new TableColumn { Name = x.Name, PropertyInfo = x }); 26 | 27 | var model = await Http.GetFromJsonAsync>($"/rest/{ApiPath}/list") ?? new List(); 28 | 29 | foreach (var item in model) 30 | { 31 | var row = new TableRow(item); 32 | 33 | foreach (var column in Data.Columns) 34 | { 35 | var value = column.PropertyInfo.GetValue(item); 36 | row.Values.Add(new TableCell { Value = value }); 37 | } 38 | 39 | Data.Rows.Add(row); 40 | } 41 | } 42 | 43 | public string GetDetailUrl(int id) 44 | => $"{Navigation.ToAbsoluteUri(Navigation.Uri).LocalPath}/{id}"; 45 | } 46 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/Model/Table.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class Table 3 | { 4 | public IEnumerable Columns { get; set; } = new List(); 5 | public List> Rows { get; set; } = new(); 6 | } 7 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableCell.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableCell 3 | { 4 | public object? Value { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | namespace MediaLibrary.Client.Shared.Model; 3 | public class TableColumn 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public PropertyInfo PropertyInfo { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableRow.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableRow 3 | { 4 | public TableRow(TItem originValue) 5 | { 6 | OriginValue = originValue; 7 | } 8 | 9 | public List Values { get; set; } = new(); 10 | public TItem OriginValue { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared 11 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Controllers/MovieController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class MovieController : BaseController 6 | { 7 | public MovieController(MovieService service) : base(service, "/movies") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Controllers/PersonController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class PersonController : BaseController 6 | { 7 | public PersonController(PersonService service) : base(service, "/persons") 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class BaseEntity 5 | { 6 | [Key] 7 | public int Id { get; set; } 8 | public string Name { get; set; } = string.Empty; 9 | } 10 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/MediaLibraryDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class MediaLibraryDbContext : DbContext 5 | { 6 | public MediaLibraryDbContext() { } 7 | 8 | public MediaLibraryDbContext(DbContextOptions options) : base(options) { } 9 | 10 | #nullable disable 11 | public DbSet Persons { get; set; } 12 | public DbSet Movies { get; set; } 13 | #nullable enable 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.Entity(b => 18 | { 19 | b.Property(x => x.Name).IsRequired(); 20 | b.Navigation(x => x.Movies).AutoInclude(); 21 | }); 22 | 23 | modelBuilder.Entity(b => 24 | { 25 | b.Property(x => x.Name).IsRequired(); 26 | b.HasOne(x => x.Director).WithMany().HasForeignKey(x => x.DirectorId).OnDelete(DeleteBehavior.Restrict); 27 | b.HasOne(x => x.MusicComposer).WithMany().HasForeignKey(x => x.MusicComposerId).OnDelete(DeleteBehavior.Restrict); 28 | b.Navigation(x => x.Director).AutoInclude(); 29 | b.Navigation(x => x.MusicComposer).AutoInclude(); 30 | b.Navigation(x => x.Actors).AutoInclude(); 31 | }); 32 | 33 | modelBuilder.Entity(b => 34 | { 35 | b.HasKey(x => new { x.Category, x.MovieId }); 36 | }); 37 | 38 | modelBuilder.Entity(b => 39 | { 40 | b.HasKey(x => new { x.MovieId, x.PersonId }); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/Movie.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Movie : BaseEntity 4 | { 5 | public List Categories { get; set; } = new List(); 6 | public int Year { get; set; } 7 | public string? Description { get; set; } 8 | public Person? Director { get; set; } 9 | public int? DirectorId { get; set; } 10 | public Person? MusicComposer { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public List Actors { get; set; } = new List(); 13 | } 14 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/MovieActor.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class MovieActor 4 | { 5 | public int MovieId { get; set; } 6 | public Movie Movie { get; set; } = null!; 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } = null!; 9 | } 10 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/MovieCategory.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Shared; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | 5 | public class MovieCategory 6 | { 7 | public int MovieId { get; set; } 8 | public Movie Movie { get; set; } = null!; 9 | public CategoryType Category { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Data/Person.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Person : BaseEntity 4 | { 5 | public DateTime BirthDay { get; set; } 6 | public string BirthPlace { get; set; } = string.Empty; 7 | public string Biography { get; set; } = string.Empty; 8 | public List Movies { get; set; } = new List(); 9 | } 10 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Migrations/20220512144428_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace MediaLibrary.Server.Migrations 7 | { 8 | public partial class InitialMigration : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Persons", 14 | columns: table => new 15 | { 16 | Id = table.Column(type: "int", nullable: false) 17 | .Annotation("SqlServer:Identity", "1, 1"), 18 | BirthDay = table.Column(type: "datetime2", nullable: false), 19 | BirthPlace = table.Column(type: "nvarchar(max)", nullable: false), 20 | Biography = table.Column(type: "nvarchar(max)", nullable: false), 21 | Name = table.Column(type: "nvarchar(max)", nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Persons", x => x.Id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "Persons"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server; 2 | using MediaLibrary.Server.Data; 3 | using MediaLibrary.Server.Services; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | builder.Services.AddDbContext(options => 8 | { 9 | options.UseSqlServer(builder.Configuration.GetConnectionString("MediaLibrary")); 10 | 11 | #if DEBUG 12 | options.EnableDetailedErrors(); 13 | options.EnableSensitiveDataLogging(); 14 | #endif 15 | }); 16 | 17 | // Add services to the container. 18 | 19 | builder.Services.AddControllersWithViews(); 20 | builder.Services.AddRazorPages(); 21 | builder.Services.AddAutoMapper(typeof(MapperProfile)); 22 | builder.Services.AddTransient(); 23 | builder.Services.AddTransient(); 24 | 25 | var app = builder.Build(); 26 | 27 | // Configure the HTTP request pipeline. 28 | if (app.Environment.IsDevelopment()) 29 | { 30 | app.UseWebAssemblyDebugging(); 31 | } 32 | else 33 | { 34 | app.UseExceptionHandler("/Error"); 35 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 36 | app.UseHsts(); 37 | } 38 | 39 | app.UseHttpsRedirection(); 40 | 41 | app.UseBlazorFrameworkFiles(); 42 | app.UseStaticFiles(); 43 | 44 | app.UseRouting(); 45 | 46 | 47 | app.MapRazorPages(); 48 | app.MapControllers(); 49 | app.MapFallbackToFile("index.html"); 50 | 51 | app.Run(); 52 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Services/MovieService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | 6 | public class MovieService : BaseService 7 | { 8 | public MovieService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/Services/PersonService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | public class PersonService : BaseService 6 | { 7 | public PersonService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Shared/CategoryType.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared; 2 | public enum CategoryType 3 | { 4 | Action = 0, 5 | Comedy = 1, 6 | Drama = 2, 7 | Fantasy = 3, 8 | SciFi = 4, 9 | Horror = 5, 10 | Mystery = 6, 11 | Romance = 7, 12 | Thriller = 8, 13 | Western = 9 14 | } 15 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Shared/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | string Name { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Shared/Model/MovieModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public class MovieModel : IModel 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = string.Empty; 7 | public List Categories { get; set; } = new List(); 8 | public int Year { get; set; } 9 | public string? Description { get; set; } 10 | public int? DirectorId { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public int[] ActorIds { get; set; } = Array.Empty(); 13 | } -------------------------------------------------------------------------------- /ch4/demo/MediaLibrary/MediaLibrary/Shared/Model/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Shared.Model; 4 | 5 | public class PersonModel : IModel 6 | { 7 | public int Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } = string.Empty; 11 | public DateTime BirthDay { get; set; } 12 | public string BirthPlace { get; set; } = string.Empty; 13 | public string Biography { get; set; } = string.Empty; 14 | public int[] MoviesIds { get; set; } = Array.Empty(); 15 | } 16 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary.Contracts/Clients/IContractClient.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | 3 | namespace MediaLibrary.Contracts; 4 | 5 | public interface IContractClient 6 | where TItem : class, new() 7 | { 8 | AsyncUnaryCall CreateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 9 | AsyncUnaryCall UpdateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 10 | AsyncUnaryCall GetAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 11 | AsyncServerStreamingCall GetList(Empty request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 12 | AsyncUnaryCall DeleteAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 13 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary.Contracts/Clients/PersonContract.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Contracts; 2 | public partial class PersonContract 3 | { 4 | public partial class PersonContractClient : IContractClient 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary.Contracts/MediaLibrary.Contracts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary.Contracts/person.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | import "google/protobuf/timestamp.proto"; 5 | import "shared.proto"; 6 | 7 | service PersonContract { 8 | rpc Create(Person) returns (contracts.shared.CreateResponse); 9 | rpc Update(Person) returns (contracts.shared.GenericResponse); 10 | rpc Get(contracts.shared.ItemRequest) returns (Person); 11 | rpc GetList(contracts.shared.Empty) returns (stream Person); 12 | rpc Delete(contracts.shared.ItemRequest) returns (contracts.shared.GenericResponse); 13 | } 14 | 15 | message Person { 16 | int32 id = 1; 17 | string name = 2; 18 | google.protobuf.Timestamp birthDay = 3; 19 | string birthPlace = 4; 20 | string biography = 5; 21 | repeated int32 moviesIds = 6; 22 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary.Contracts/shared.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts.shared; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | 5 | message ItemRequest { 6 | int32 id = 1; 7 | } 8 | 9 | message CreateResponse { 10 | int32 id = 1; 11 | string path = 2; 12 | } 13 | 14 | message GenericResponse { 15 | bool success = 1; 16 | } 17 | 18 | message Empty {} -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Pages/MovieDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/movies/{Id:int}" 2 | 3 |

4 |

5 |

6 |

7 |

8 |
9 | 10 | @code { 11 | [Parameter] 12 | public int Id { get; set; } 13 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Pages/MovieList.razor: -------------------------------------------------------------------------------- 1 | @page "/movies" 2 | 3 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Pages/PersonDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/persons/{Id:int}" 2 | 3 |

4 |

5 |

6 |

7 |
8 | 9 | @code { 10 | [Parameter] 11 | public int Id { get; set; } 12 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Pages/PersonList.razor: -------------------------------------------------------------------------------- 1 | @page "/persons" 2 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Net.Client; 2 | using Grpc.Net.Client.Web; 3 | using MediaLibrary.Client; 4 | using MediaLibrary.Contracts; 5 | using Microsoft.AspNetCore.Components; 6 | using Microsoft.AspNetCore.Components.Web; 7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 8 | 9 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 10 | builder.RootComponents.Add("#app"); 11 | builder.RootComponents.Add("head::after"); 12 | 13 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 14 | builder.Services.AddAutoMapper(typeof(MediaLibrary.Shared.SharedMapperProfile)); 15 | 16 | builder.Services.AddSingleton(s => 17 | { 18 | var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); 19 | var baseUri = s.GetRequiredService().BaseUri; 20 | var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient }); 21 | var client = new PersonContract.PersonContractClient(channel); 22 | return client; 23 | }); 24 | 25 | await builder.Build().RunAsync(); 26 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/DataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @attribute [CascadingTypeParameter(nameof(TModel))] 3 | 4 | 5 | 6 | 7 | @ChildContent(Model) 8 | 9 | 10 |
11 | 12 | 13 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 14 | { 15 |
@_errorMessage
16 | } 17 |
-------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/DataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @using Microsoft.AspNetCore.Components.Web.Virtualization 3 | New 4 | 5 | 6 | 7 | @foreach (var column in Data.Columns) 8 | { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach (var data in item.Values) 18 | { 19 | 29 | } 30 | 31 | 36 | 37 | 38 | 39 |
@column.Name
20 | @if (data.Value is null) 21 | { 22 | --- 23 | } 24 | else 25 | { 26 | @data.Value 27 | } 28 | 32 |
33 | Edit 34 |
35 |
-------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/Model/Table.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class Table 3 | { 4 | public IEnumerable Columns { get; set; } = new List(); 5 | public List> Rows { get; set; } = new(); 6 | } 7 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/Model/TableCell.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableCell 3 | { 4 | public object? Value { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/Model/TableColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | namespace MediaLibrary.Client.Shared.Model; 3 | public class TableColumn 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public PropertyInfo PropertyInfo { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/Model/TableRow.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableRow 3 | { 4 | public TableRow(TItem originValue) 5 | { 6 | OriginValue = originValue; 7 | } 8 | 9 | public List Values { get; set; } = new(); 10 | public TItem OriginValue { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Contracts/IContractService.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using MediaLibrary.Contracts; 3 | 4 | namespace MediaLibrary.Server.Contracts; 5 | 6 | public interface IContractService 7 | { 8 | Task Create(T request, ServerCallContext context); 9 | Task Delete(ItemRequest request, ServerCallContext context); 10 | Task Get(ItemRequest request, ServerCallContext context); 11 | Task GetList(Empty request, IServerStreamWriter responseStream, ServerCallContext context); 12 | Task Update(Person request, ServerCallContext context); 13 | } 14 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Controllers/MovieController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class MovieController : BaseController 6 | { 7 | public MovieController(MovieService service) : base(service, "/movies") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Controllers/PersonController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class PersonController : BaseController 6 | { 7 | public PersonController(PersonService service) : base(service, "/persons") 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class BaseEntity 5 | { 6 | [Key] 7 | public int Id { get; set; } 8 | public string Name { get; set; } = string.Empty; 9 | } 10 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/MediaLibraryDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class MediaLibraryDbContext : DbContext 5 | { 6 | public MediaLibraryDbContext() { } 7 | 8 | public MediaLibraryDbContext(DbContextOptions options) : base(options) { } 9 | 10 | #nullable disable 11 | public DbSet Persons { get; set; } 12 | public DbSet Movies { get; set; } 13 | #nullable enable 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.Entity(b => 18 | { 19 | b.Property(x => x.Name).IsRequired(); 20 | b.Navigation(x => x.Movies).AutoInclude(); 21 | }); 22 | 23 | modelBuilder.Entity(b => 24 | { 25 | b.Property(x => x.Name).IsRequired(); 26 | b.HasOne(x => x.Director).WithMany().HasForeignKey(x => x.DirectorId).OnDelete(DeleteBehavior.Restrict); 27 | b.HasOne(x => x.MusicComposer).WithMany().HasForeignKey(x => x.MusicComposerId).OnDelete(DeleteBehavior.Restrict); 28 | b.Navigation(x => x.Director).AutoInclude(); 29 | b.Navigation(x => x.MusicComposer).AutoInclude(); 30 | b.Navigation(x => x.Actors).AutoInclude(); 31 | }); 32 | 33 | modelBuilder.Entity(b => 34 | { 35 | b.HasKey(x => new { x.Category, x.MovieId }); 36 | }); 37 | 38 | modelBuilder.Entity(b => 39 | { 40 | b.HasKey(x => new { x.MovieId, x.PersonId }); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/Movie.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Movie : BaseEntity 4 | { 5 | public List Categories { get; set; } = new List(); 6 | public int Year { get; set; } 7 | public string? Description { get; set; } 8 | public Person? Director { get; set; } 9 | public int? DirectorId { get; set; } 10 | public Person? MusicComposer { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public List Actors { get; set; } = new List(); 13 | } 14 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/MovieActor.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class MovieActor 4 | { 5 | public int MovieId { get; set; } 6 | public Movie Movie { get; set; } = null!; 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } = null!; 9 | } 10 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/MovieCategory.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Shared; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | 5 | public class MovieCategory 6 | { 7 | public int MovieId { get; set; } 8 | public Movie Movie { get; set; } = null!; 9 | public CategoryType Category { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Data/Person.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Person : BaseEntity 4 | { 5 | public DateTime BirthDay { get; set; } 6 | public string BirthPlace { get; set; } = string.Empty; 7 | public string Biography { get; set; } = string.Empty; 8 | public List Movies { get; set; } = new List(); 9 | } 10 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Migrations/20220512144428_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace MediaLibrary.Server.Migrations 7 | { 8 | public partial class InitialMigration : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Persons", 14 | columns: table => new 15 | { 16 | Id = table.Column(type: "int", nullable: false) 17 | .Annotation("SqlServer:Identity", "1, 1"), 18 | BirthDay = table.Column(type: "datetime2", nullable: false), 19 | BirthPlace = table.Column(type: "nvarchar(max)", nullable: false), 20 | Biography = table.Column(type: "nvarchar(max)", nullable: false), 21 | Name = table.Column(type: "nvarchar(max)", nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Persons", x => x.Id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "Persons"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Services/MovieService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | 6 | public class MovieService : BaseService 7 | { 8 | public MovieService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/Services/PersonService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | public class PersonService : BaseService 6 | { 7 | public PersonService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/CategoryType.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared; 2 | public enum CategoryType 3 | { 4 | Action = 0, 5 | Comedy = 1, 6 | Drama = 2, 7 | Fantasy = 3, 8 | SciFi = 4, 9 | Horror = 5, 10 | Mystery = 6, 11 | Romance = 7, 12 | Thriller = 8, 13 | Western = 9 14 | } 15 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | string Name { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/Model/MovieModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public class MovieModel : IModel 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = string.Empty; 7 | public List Categories { get; set; } = new List(); 8 | public int Year { get; set; } 9 | public string? Description { get; set; } 10 | public int? DirectorId { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public int[] ActorIds { get; set; } = Array.Empty(); 13 | } -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/Model/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Shared.Model; 4 | 5 | public class PersonModel : IModel 6 | { 7 | public int Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } = string.Empty; 11 | public DateTime BirthDay { get; set; } 12 | public string BirthPlace { get; set; } = string.Empty; 13 | public string Biography { get; set; } = string.Empty; 14 | public int[] MoviesIds { get; set; } = Array.Empty(); 15 | } 16 | -------------------------------------------------------------------------------- /ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Shared/SharedMapperProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Google.Protobuf.WellKnownTypes; 3 | 4 | namespace MediaLibrary.Shared; 5 | public class SharedMapperProfile : Profile 6 | { 7 | public SharedMapperProfile() 8 | { 9 | CreateMap() 10 | .ConvertUsing(x => Timestamp.FromDateTime(DateTime.SpecifyKind(x, DateTimeKind.Utc))); 11 | 12 | CreateMap() 13 | .ConvertUsing(x => x.ToDateTime()); 14 | 15 | CreateMap().ReverseMap(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/Clients/IContractClient.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | 3 | namespace MediaLibrary.Contracts; 4 | 5 | public interface IContractClient 6 | where TItem : class, new() 7 | { 8 | AsyncUnaryCall CreateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 9 | AsyncUnaryCall UpdateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 10 | AsyncUnaryCall GetAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 11 | AsyncServerStreamingCall GetList(Empty request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 12 | AsyncUnaryCall DeleteAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 13 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/Clients/MovieContract.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Contracts; 2 | public partial class MovieContract 3 | { 4 | public partial class MovieContractClient : IContractClient 5 | { } 6 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/Clients/PersonContract.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Contracts; 2 | public partial class PersonContract 3 | { 4 | public partial class PersonContractClient : IContractClient 5 | { } 6 | } 7 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/MediaLibrary.Contracts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/movie.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | import "google/protobuf/timestamp.proto"; 5 | import "google/protobuf/wrappers.proto"; 6 | import "shared.proto"; 7 | 8 | service MovieContract { 9 | rpc Create(Movie) returns (contracts.shared.CreateResponse); 10 | rpc Update(Movie) returns (contracts.shared.GenericResponse); 11 | rpc Get(contracts.shared.ItemRequest) returns (Movie); 12 | rpc GetList(contracts.shared.Empty) returns (stream Movie); 13 | rpc Delete(contracts.shared.ItemRequest) returns (contracts.shared.GenericResponse); 14 | } 15 | 16 | message Movie { 17 | int32 id = 1; 18 | string name = 2; 19 | CategoryType categories = 3; 20 | int32 year = 4; 21 | string description = 5; 22 | google.protobuf.Int32Value directorId = 6; 23 | google.protobuf.Int32Value musicComposerId = 7; 24 | repeated int32 actorIds = 8; 25 | } 26 | 27 | enum CategoryType { 28 | Action = 0; 29 | Comedy = 1; 30 | Drama = 2; 31 | Fantasy = 3; 32 | SciFi = 4; 33 | Horror = 5; 34 | Mystery = 6; 35 | Romance = 7; 36 | Thriller = 8; 37 | Western = 9; 38 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/person.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | import "google/protobuf/timestamp.proto"; 5 | import "shared.proto"; 6 | 7 | service PersonContract { 8 | rpc Create(Person) returns (contracts.shared.CreateResponse); 9 | rpc Update(Person) returns (contracts.shared.GenericResponse); 10 | rpc Get(contracts.shared.ItemRequest) returns (Person); 11 | rpc GetList(contracts.shared.Empty) returns (stream Person); 12 | rpc Delete(contracts.shared.ItemRequest) returns (contracts.shared.GenericResponse); 13 | } 14 | 15 | message Person { 16 | int32 id = 1; 17 | string name = 2; 18 | google.protobuf.Timestamp birthDay = 3; 19 | string birthPlace = 4; 20 | string biography = 5; 21 | repeated int32 moviesIds = 6; 22 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary.Contracts/shared.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts.shared; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | 5 | message ItemRequest { 6 | int32 id = 1; 7 | } 8 | 9 | message CreateResponse { 10 | int32 id = 1; 11 | string path = 2; 12 | } 13 | 14 | message GenericResponse { 15 | bool success = 1; 16 | } 17 | 18 | message Empty {} -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Pages/MovieDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/movies/{Id:int}" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | 5 |

6 |

7 |

8 |

9 |

10 |
11 | 12 | @code { 13 | [Parameter] 14 | public int Id { get; set; } 15 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Pages/MovieList.razor: -------------------------------------------------------------------------------- 1 | @page "/movies" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Pages/PersonDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/persons/{Id:int}" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | 5 |

6 |

7 |

8 |

9 |
10 | 11 | @code { 12 | [Parameter] 13 | public int Id { get; set; } 14 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Pages/PersonList.razor: -------------------------------------------------------------------------------- 1 | @page "/persons" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | 8 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Net.Client; 2 | using Grpc.Net.Client.Web; 3 | using MediaLibrary.Client; 4 | using MediaLibrary.Contracts; 5 | using Microsoft.AspNetCore.Components; 6 | using Microsoft.AspNetCore.Components.Web; 7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 8 | 9 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 10 | builder.RootComponents.Add("#app"); 11 | builder.RootComponents.Add("head::after"); 12 | 13 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 14 | builder.Services.AddAutoMapper(typeof(MediaLibrary.Shared.SharedMapperProfile)); 15 | 16 | builder.Services.AddSingleton(s => 17 | { 18 | var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); 19 | var baseUri = s.GetRequiredService().BaseUri; 20 | var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient }); 21 | var client = new PersonContract.PersonContractClient(channel); 22 | return client; 23 | }); 24 | 25 | builder.Services.AddSingleton(s => 26 | { 27 | var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); 28 | var baseUri = s.GetRequiredService().BaseUri; 29 | var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient }); 30 | var client = new MovieContract.MovieContractClient(channel); 31 | return client; 32 | }); 33 | 34 | await builder.Build().RunAsync(); 35 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:56037/", 7 | "sslPort": 44353 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 18 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 19 | "dotnetRunMessages": true 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/DataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @attribute [CascadingTypeParameter(nameof(TModel))] 3 | 4 | 5 | 6 | 7 | @ChildContent(Model) 8 | 9 | 10 |
11 | 12 | 13 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 14 | { 15 |
@_errorMessage
16 | } 17 |
-------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/DataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @using Microsoft.AspNetCore.Components.Web.Virtualization 3 | New 4 | 5 | 6 | 7 | @foreach (var column in Data.Columns) 8 | { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach (var data in item.Values) 18 | { 19 | 29 | } 30 | 31 | 36 | 37 | 38 | 39 |
@column.Name
20 | @if (data.Value is null) 21 | { 22 | --- 23 | } 24 | else 25 | { 26 | @data.Value 27 | } 28 | 32 |
33 | Edit 34 |
35 |
-------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/GrpcDataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @typeparam TContractItem 3 | @typeparam TContractClient 4 | 5 | @attribute [CascadingTypeParameter(nameof(TModel))] 6 | 7 | 8 | 9 | 10 | @ChildContent(Model) 11 | 12 | 13 |
14 | 15 | 16 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 17 | { 18 |
@_errorMessage
19 | } 20 |
-------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/GrpcDataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @typeparam TContractItem 3 | @typeparam TContractClient 4 | @using Microsoft.AspNetCore.Components.Web.Virtualization 5 | New 6 | 7 | 8 | 9 | @foreach (var column in Data.Columns) 10 | { 11 | 12 | } 13 | 14 | 15 | 16 | 17 | 18 | 19 | @foreach (var data in item.Values) 20 | { 21 | 31 | } 32 | 33 | 38 | 39 | 40 | 41 |
@column.Name
22 | @if (data.Value is null) 23 | { 24 | --- 25 | } 26 | else 27 | { 28 | @data.Value 29 | } 30 | 34 |
35 | Edit 36 |
37 |
42 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/Model/Table.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class Table 3 | { 4 | public IEnumerable Columns { get; set; } = new List(); 5 | public List> Rows { get; set; } = new(); 6 | } 7 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/Model/TableCell.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableCell 3 | { 4 | public object? Value { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/Model/TableColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | namespace MediaLibrary.Client.Shared.Model; 3 | public class TableColumn 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public PropertyInfo PropertyInfo { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/Model/TableRow.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableRow 3 | { 4 | public TableRow(TItem originValue) 5 | { 6 | OriginValue = originValue; 7 | } 8 | 9 | public List Values { get; set; } = new(); 10 | public TItem OriginValue { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared 11 | @using AutoMapper -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Contracts/IContractService.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using MediaLibrary.Contracts; 3 | 4 | namespace MediaLibrary.Server.Contracts; 5 | 6 | public interface IContractService 7 | { 8 | Task Create(T request, ServerCallContext context); 9 | Task Delete(ItemRequest request, ServerCallContext context); 10 | Task Get(ItemRequest request, ServerCallContext context); 11 | Task GetList(Empty request, IServerStreamWriter responseStream, ServerCallContext context); 12 | Task Update(T request, ServerCallContext context); 13 | } 14 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Controllers/MovieController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class MovieController : BaseController 6 | { 7 | public MovieController(MovieService service) : base(service, "/movies") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Controllers/PersonController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class PersonController : BaseController 6 | { 7 | public PersonController(PersonService service) : base(service, "/persons") 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class BaseEntity 5 | { 6 | [Key] 7 | public int Id { get; set; } 8 | public string Name { get; set; } = string.Empty; 9 | } 10 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/MediaLibraryDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class MediaLibraryDbContext : DbContext 5 | { 6 | public MediaLibraryDbContext() { } 7 | 8 | public MediaLibraryDbContext(DbContextOptions options) : base(options) { } 9 | 10 | #nullable disable 11 | public DbSet Persons { get; set; } 12 | public DbSet Movies { get; set; } 13 | #nullable enable 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.Entity(b => 18 | { 19 | b.Property(x => x.Name).IsRequired(); 20 | b.Navigation(x => x.Movies).AutoInclude(); 21 | }); 22 | 23 | modelBuilder.Entity(b => 24 | { 25 | b.Property(x => x.Name).IsRequired(); 26 | b.HasOne(x => x.Director).WithMany().HasForeignKey(x => x.DirectorId).OnDelete(DeleteBehavior.Restrict); 27 | b.HasOne(x => x.MusicComposer).WithMany().HasForeignKey(x => x.MusicComposerId).OnDelete(DeleteBehavior.Restrict); 28 | b.Navigation(x => x.Director).AutoInclude(); 29 | b.Navigation(x => x.MusicComposer).AutoInclude(); 30 | b.Navigation(x => x.Actors).AutoInclude(); 31 | }); 32 | 33 | modelBuilder.Entity(b => 34 | { 35 | b.HasKey(x => new { x.Category, x.MovieId }); 36 | }); 37 | 38 | modelBuilder.Entity(b => 39 | { 40 | b.HasKey(x => new { x.MovieId, x.PersonId }); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/Movie.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Movie : BaseEntity 4 | { 5 | public List Categories { get; set; } = new List(); 6 | public int Year { get; set; } 7 | public string? Description { get; set; } 8 | public Person? Director { get; set; } 9 | public int? DirectorId { get; set; } 10 | public Person? MusicComposer { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public List Actors { get; set; } = new List(); 13 | } 14 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/MovieActor.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class MovieActor 4 | { 5 | public int MovieId { get; set; } 6 | public Movie Movie { get; set; } = null!; 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } = null!; 9 | } 10 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/MovieCategory.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Shared; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | 5 | public class MovieCategory 6 | { 7 | public int MovieId { get; set; } 8 | public Movie Movie { get; set; } = null!; 9 | public CategoryType Category { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Data/Person.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class Person : BaseEntity 4 | { 5 | public DateTime BirthDay { get; set; } 6 | public string BirthPlace { get; set; } = string.Empty; 7 | public string Biography { get; set; } = string.Empty; 8 | public List Movies { get; set; } = new List(); 9 | } 10 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Migrations/20220512144428_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace MediaLibrary.Server.Migrations 7 | { 8 | public partial class InitialMigration : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Persons", 14 | columns: table => new 15 | { 16 | Id = table.Column(type: "int", nullable: false) 17 | .Annotation("SqlServer:Identity", "1, 1"), 18 | BirthDay = table.Column(type: "datetime2", nullable: false), 19 | BirthPlace = table.Column(type: "nvarchar(max)", nullable: false), 20 | Biography = table.Column(type: "nvarchar(max)", nullable: false), 21 | Name = table.Column(type: "nvarchar(max)", nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Persons", x => x.Id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "Persons"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Services/MovieService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | 6 | public class MovieService : BaseService 7 | { 8 | public MovieService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/Services/PersonService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | public class PersonService : BaseService 6 | { 7 | public PersonService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/CategoryType.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared; 2 | public enum CategoryType 3 | { 4 | Action = 0, 5 | Comedy = 1, 6 | Drama = 2, 7 | Fantasy = 3, 8 | SciFi = 4, 9 | Horror = 5, 10 | Mystery = 6, 11 | Romance = 7, 12 | Thriller = 8, 13 | Western = 9 14 | } 15 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | string Name { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/Model/MovieModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public class MovieModel : IModel 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = string.Empty; 7 | public List Categories { get; set; } = new List(); 8 | public int Year { get; set; } 9 | public string? Description { get; set; } 10 | public int? DirectorId { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public int[] ActorIds { get; set; } = Array.Empty(); 13 | } -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/Model/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Shared.Model; 4 | 5 | public class PersonModel : IModel 6 | { 7 | public int Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } = string.Empty; 11 | public DateTime BirthDay { get; set; } 12 | public string BirthPlace { get; set; } = string.Empty; 13 | public string Biography { get; set; } = string.Empty; 14 | public int[] MoviesIds { get; set; } = Array.Empty(); 15 | } 16 | -------------------------------------------------------------------------------- /ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Shared/SharedMapperProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Google.Protobuf.WellKnownTypes; 3 | 4 | namespace MediaLibrary.Shared; 5 | public class SharedMapperProfile : Profile 6 | { 7 | public SharedMapperProfile() 8 | { 9 | CreateMap() 10 | .ConvertUsing(x => Timestamp.FromDateTime(DateTime.SpecifyKind(x, DateTimeKind.Utc))); 11 | 12 | CreateMap() 13 | .ConvertUsing(x => x.ToDateTime()); 14 | 15 | CreateMap().ReverseMap(); 16 | CreateMap().ReverseMap(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/Clients/IContractClient.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | 3 | namespace MediaLibrary.Contracts; 4 | 5 | public interface IContractClient 6 | where TItem : class, new() 7 | { 8 | AsyncUnaryCall CreateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 9 | AsyncUnaryCall UpdateAsync(TItem request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 10 | AsyncUnaryCall GetAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 11 | AsyncServerStreamingCall GetList(Empty request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 12 | AsyncUnaryCall DeleteAsync(ItemRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default); 13 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/Clients/MovieContract.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Contracts; 2 | public partial class MovieContract 3 | { 4 | public partial class MovieContractClient : IContractClient 5 | { } 6 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/Clients/PersonContract.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Contracts; 2 | public partial class PersonContract 3 | { 4 | public partial class PersonContractClient : IContractClient 5 | { } 6 | } 7 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/MediaLibrary.Contracts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/movie.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | import "google/protobuf/timestamp.proto"; 5 | import "google/protobuf/wrappers.proto"; 6 | import "shared.proto"; 7 | 8 | service MovieContract { 9 | rpc Create(Movie) returns (contracts.shared.CreateResponse); 10 | rpc Update(Movie) returns (contracts.shared.GenericResponse); 11 | rpc Get(contracts.shared.ItemRequest) returns (Movie); 12 | rpc GetList(contracts.shared.Empty) returns (stream Movie); 13 | rpc Delete(contracts.shared.ItemRequest) returns (contracts.shared.GenericResponse); 14 | } 15 | 16 | message Movie { 17 | int32 id = 1; 18 | string name = 2; 19 | repeated CategoryType categories = 3; 20 | int32 year = 4; 21 | string description = 5; 22 | google.protobuf.Int32Value directorId = 6; 23 | google.protobuf.Int32Value musicComposerId = 7; 24 | repeated int32 actorIds = 8; 25 | } 26 | 27 | enum CategoryType { 28 | Action = 0; 29 | Comedy = 1; 30 | Drama = 2; 31 | Fantasy = 3; 32 | SciFi = 4; 33 | Horror = 5; 34 | Mystery = 6; 35 | Romance = 7; 36 | Thriller = 8; 37 | Western = 9; 38 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/person.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | import "google/protobuf/timestamp.proto"; 5 | import "shared.proto"; 6 | 7 | service PersonContract { 8 | rpc Create(Person) returns (contracts.shared.CreateResponse); 9 | rpc Update(Person) returns (contracts.shared.GenericResponse); 10 | rpc Get(contracts.shared.ItemRequest) returns (Person); 11 | rpc GetList(contracts.shared.Empty) returns (stream Person); 12 | rpc Delete(contracts.shared.ItemRequest) returns (contracts.shared.GenericResponse); 13 | } 14 | 15 | message Person { 16 | int32 id = 1; 17 | string name = 2; 18 | google.protobuf.Timestamp birthDay = 3; 19 | string birthPlace = 4; 20 | string biography = 5; 21 | repeated int32 moviesIds = 6; 22 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Contracts/shared.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package contracts.shared; 3 | option csharp_namespace = "MediaLibrary.Contracts"; 4 | 5 | message ItemRequest { 6 | int32 id = 1; 7 | } 8 | 9 | message CreateResponse { 10 | int32 id = 1; 11 | string path = 2; 12 | } 13 | 14 | message GenericResponse { 15 | bool success = 1; 16 | } 17 | 18 | message Empty {} -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Generators/ClassData.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Syntax; 2 | 3 | namespace MediaLibrary.Generators; 4 | 5 | internal class ClassData 6 | { 7 | public ClassData(ClassDeclarationSyntax node, AttributeSyntax attribute) 8 | { 9 | Node = node; 10 | Attribute = attribute; 11 | } 12 | 13 | public ClassDeclarationSyntax Node { get; set; } 14 | public AttributeSyntax Attribute { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Generators/MediaLibrary.Generators.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary.Generators/SyntaxReceiver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace MediaLibrary.Generators; 7 | 8 | internal class SyntaxReceiver : ISyntaxReceiver 9 | { 10 | private const string _attributeName = "UseCustomGenerator"; 11 | public List Nodes { get; } = new List(); 12 | 13 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode) 14 | { 15 | if (syntaxNode is not ClassDeclarationSyntax declarationSyntax || !declarationSyntax.AttributeLists.Any()) 16 | { 17 | return; 18 | } 19 | 20 | var attrList = declarationSyntax.AttributeLists 21 | .Select(x => x.Attributes) 22 | .SelectMany(x => x) 23 | .Where(x => x.Name.ToString() == _attributeName) 24 | .ToList(); 25 | 26 | var customGenerator = attrList.FirstOrDefault(x => x.Name.ToString() == _attributeName); 27 | 28 | if (customGenerator is null) 29 | { 30 | return; 31 | } 32 | 33 | Nodes.Add(new ClassData(declarationSyntax, customGenerator)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/MediaLibrary.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Pages/MovieList.razor: -------------------------------------------------------------------------------- 1 | @page "/movies" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Pages/PersonDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/persons/{Id:int}" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | 5 |

6 |

7 |

8 |

9 |
10 | 11 | @code { 12 | [Parameter] 13 | public int Id { get; set; } 14 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Pages/PersonList.razor: -------------------------------------------------------------------------------- 1 | @page "/persons" 2 | @using MediaLibrary.Contracts 3 | @using MediaLibrary.Shared.Model 4 | 8 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Net.Client; 2 | using Grpc.Net.Client.Web; 3 | using MediaLibrary.Client; 4 | using MediaLibrary.Contracts; 5 | using Microsoft.AspNetCore.Components; 6 | using Microsoft.AspNetCore.Components.Web; 7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 8 | 9 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 10 | builder.RootComponents.Add("#app"); 11 | builder.RootComponents.Add("head::after"); 12 | 13 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 14 | builder.Services.AddAutoMapper(typeof(MediaLibrary.Shared.SharedMapperProfile)); 15 | 16 | builder.Services.AddSingleton(s => 17 | { 18 | var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); 19 | var baseUri = s.GetRequiredService().BaseUri; 20 | var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient }); 21 | var client = new PersonContract.PersonContractClient(channel); 22 | return client; 23 | }); 24 | 25 | builder.Services.AddSingleton(s => 26 | { 27 | var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); 28 | var baseUri = s.GetRequiredService().BaseUri; 29 | var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient }); 30 | var client = new MovieContract.MovieContractClient(channel); 31 | return client; 32 | }); 33 | 34 | await builder.Build().RunAsync(); 35 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:56037/", 7 | "sslPort": 44353 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 18 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 19 | "dotnetRunMessages": true 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/DataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @attribute [CascadingTypeParameter(nameof(TModel))] 3 | 4 | 5 | 6 | 7 | @ChildContent(Model) 8 | 9 | 10 |
11 | 12 | 13 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 14 | { 15 |
@_errorMessage
16 | } 17 |
-------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/DataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @using Microsoft.AspNetCore.Components.Web.Virtualization 3 | New 4 | 5 | 6 | 7 | @foreach (var column in Data.Columns) 8 | { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach (var data in item.Values) 18 | { 19 | 29 | } 30 | 31 | 36 | 37 | 38 | 39 |
@column.Name
20 | @if (data.Value is null) 21 | { 22 | --- 23 | } 24 | else 25 | { 26 | @data.Value 27 | } 28 | 32 |
33 | Edit 34 |
35 |
-------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/GrpcDataForm.razor: -------------------------------------------------------------------------------- 1 | @typeparam TModel 2 | @typeparam TContractItem 3 | @typeparam TContractClient 4 | 5 | @attribute [CascadingTypeParameter(nameof(TModel))] 6 | 7 | 8 | 9 | 10 | @ChildContent(Model) 11 | 12 | 13 |
14 | 15 | 16 | @if (!string.IsNullOrWhiteSpace(_errorMessage)) 17 | { 18 |
@_errorMessage
19 | } 20 |
-------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/GrpcDataView.razor: -------------------------------------------------------------------------------- 1 | @typeparam TItem 2 | @typeparam TContractItem 3 | @typeparam TContractClient 4 | @using Microsoft.AspNetCore.Components.Web.Virtualization 5 | New 6 | 7 | 8 | 9 | @foreach (var column in Data.Columns) 10 | { 11 | 12 | } 13 | 14 | 15 | 16 | 17 | 18 | 19 | @foreach (var data in item.Values) 20 | { 21 | 31 | } 32 | 33 | 38 | 39 | 40 | 41 |
@column.Name
22 | @if (data.Value is null) 23 | { 24 | --- 25 | } 26 | else 27 | { 28 | @data.Value 29 | } 30 | 34 |
35 | Edit 36 |
37 |
42 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/Model/Table.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class Table 3 | { 4 | public IEnumerable Columns { get; set; } = new List(); 5 | public List> Rows { get; set; } = new(); 6 | } 7 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableCell.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableCell 3 | { 4 | public object? Value { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | namespace MediaLibrary.Client.Shared.Model; 3 | public class TableColumn 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public PropertyInfo PropertyInfo { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/Model/TableRow.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Client.Shared.Model; 2 | public class TableRow 3 | { 4 | public TableRow(TItem originValue) 5 | { 6 | OriginValue = originValue; 7 | } 8 | 9 | public List Values { get; set; } = new(); 10 | public TItem OriginValue { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using MediaLibrary.Client 10 | @using MediaLibrary.Client.Shared 11 | @using AutoMapper -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Building-Blazor-WebAssembly-Applications-with-gRPC/76b5ab253e0fc974a385b8b5d4ced3e43260741d/ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MediaLibrary 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Contracts/IContractService.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using MediaLibrary.Contracts; 3 | 4 | namespace MediaLibrary.Server.Contracts; 5 | 6 | public interface IContractService 7 | { 8 | Task Create(T request, ServerCallContext context); 9 | Task Delete(ItemRequest request, ServerCallContext context); 10 | Task Get(ItemRequest request, ServerCallContext context); 11 | Task GetList(Empty request, IServerStreamWriter responseStream, ServerCallContext context); 12 | Task Update(T request, ServerCallContext context); 13 | } 14 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Controllers/MovieController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class MovieController : BaseController 6 | { 7 | public MovieController(MovieService service) : base(service, "/movies") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Controllers/PersonController.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Server.Services; 2 | 3 | namespace MediaLibrary.Server.Controllers; 4 | 5 | public class PersonController : BaseController 6 | { 7 | public PersonController(PersonService service) : base(service, "/persons") 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class BaseEntity 5 | { 6 | [Key] 7 | public int Id { get; set; } 8 | public string Name { get; set; } = string.Empty; 9 | } 10 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/MediaLibraryDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | public class MediaLibraryDbContext : DbContext 5 | { 6 | public MediaLibraryDbContext() { } 7 | 8 | public MediaLibraryDbContext(DbContextOptions options) : base(options) { } 9 | 10 | #nullable disable 11 | public DbSet Persons { get; set; } 12 | public DbSet Movies { get; set; } 13 | #nullable enable 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder.Entity(b => 18 | { 19 | b.Property(x => x.Name).IsRequired(); 20 | b.Navigation(x => x.Movies).AutoInclude(); 21 | }); 22 | 23 | modelBuilder.Entity(b => 24 | { 25 | b.Property(x => x.Name).IsRequired(); 26 | b.HasOne(x => x.Director).WithMany().HasForeignKey(x => x.DirectorId).OnDelete(DeleteBehavior.Restrict); 27 | b.HasOne(x => x.MusicComposer).WithMany().HasForeignKey(x => x.MusicComposerId).OnDelete(DeleteBehavior.Restrict); 28 | b.Navigation(x => x.Director).AutoInclude(); 29 | b.Navigation(x => x.MusicComposer).AutoInclude(); 30 | b.Navigation(x => x.Actors).AutoInclude(); 31 | }); 32 | 33 | modelBuilder.Entity(b => 34 | { 35 | b.HasKey(x => new { x.Category, x.MovieId }); 36 | }); 37 | 38 | modelBuilder.Entity(b => 39 | { 40 | b.HasKey(x => new { x.MovieId, x.PersonId }); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/Movie.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | [UseCustomGenerator(true)] 4 | public class Movie : BaseEntity 5 | { 6 | public List Categories { get; set; } = new List(); 7 | public int Year { get; set; } 8 | public string? Description { get; set; } 9 | public Person? Director { get; set; } 10 | public int? DirectorId { get; set; } 11 | public Person? MusicComposer { get; set; } 12 | public int? MusicComposerId { get; set; } 13 | public List Actors { get; set; } = new List(); 14 | } 15 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/MovieActor.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | public class MovieActor 4 | { 5 | public int MovieId { get; set; } 6 | public Movie Movie { get; set; } = null!; 7 | public int PersonId { get; set; } 8 | public Person Person { get; set; } = null!; 9 | } 10 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/MovieCategory.cs: -------------------------------------------------------------------------------- 1 | using MediaLibrary.Shared; 2 | 3 | namespace MediaLibrary.Server.Data; 4 | 5 | public class MovieCategory 6 | { 7 | public int MovieId { get; set; } 8 | public Movie Movie { get; set; } = null!; 9 | public CategoryType Category { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Data/Person.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server.Data; 2 | 3 | [UseCustomGenerator(false)] 4 | public class Person : BaseEntity 5 | { 6 | public DateTime BirthDay { get; set; } 7 | public string BirthPlace { get; set; } = string.Empty; 8 | public string Biography { get; set; } = string.Empty; 9 | public List Movies { get; set; } = new List(); 10 | } 11 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/MediaLibrary.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Migrations/20220512144428_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace MediaLibrary.Server.Migrations 7 | { 8 | public partial class InitialMigration : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Persons", 14 | columns: table => new 15 | { 16 | Id = table.Column(type: "int", nullable: false) 17 | .Annotation("SqlServer:Identity", "1, 1"), 18 | BirthDay = table.Column(type: "datetime2", nullable: false), 19 | BirthPlace = table.Column(type: "nvarchar(max)", nullable: false), 20 | Biography = table.Column(type: "nvarchar(max)", nullable: false), 21 | Name = table.Column(type: "nvarchar(max)", nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Persons", x => x.Id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "Persons"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace MediaLibrary.Server.Pages; 6 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 7 | [IgnoreAntiforgeryToken] 8 | public class ErrorModel : PageModel 9 | { 10 | public string? RequestId { get; set; } 11 | 12 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 13 | 14 | private readonly ILogger _logger; 15 | 16 | public ErrorModel(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1232", 7 | "sslPort": 44300 8 | } 9 | }, 10 | "profiles": { 11 | "MediaLibrary.Server": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7000;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Services/MovieService.cs: -------------------------------------------------------------------------------- 1 | //using AutoMapper; 2 | //using MediaLibrary.Server.Data; 3 | 4 | //namespace MediaLibrary.Server.Services; 5 | 6 | //public partial class MovieService// : BaseService 7 | //{ 8 | // //public MovieService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 9 | // //{ 10 | // //} 11 | //} 12 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/Services/PersonService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediaLibrary.Server.Data; 3 | 4 | namespace MediaLibrary.Server.Services; 5 | public partial class PersonService// : BaseService 6 | { 7 | public PersonService(MediaLibraryDbContext dbContext, IMapper mapper) : base(dbContext, mapper) 8 | { 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/UseCustomGeneratorAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Server; 2 | [AttributeUsage(AttributeTargets.Class)] 3 | public class UseCustomGeneratorAttribute : Attribute 4 | { 5 | public bool GenerateConstructor { get; set; } 6 | 7 | public UseCustomGeneratorAttribute(bool generateConstructor) 8 | { 9 | GenerateConstructor = generateConstructor; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/CategoryType.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared; 2 | public enum CategoryType 3 | { 4 | Action = 0, 5 | Comedy = 1, 6 | Drama = 2, 7 | Fantasy = 3, 8 | SciFi = 4, 9 | Horror = 5, 10 | Mystery = 6, 11 | Romance = 7, 12 | Thriller = 8, 13 | Western = 9 14 | } 15 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/MediaLibrary.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | string Name { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/Model/MovieModel.cs: -------------------------------------------------------------------------------- 1 | namespace MediaLibrary.Shared.Model; 2 | 3 | public class MovieModel : IModel 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = string.Empty; 7 | public List Categories { get; set; } = new List(); 8 | public int Year { get; set; } 9 | public string? Description { get; set; } 10 | public int? DirectorId { get; set; } 11 | public int? MusicComposerId { get; set; } 12 | public int[] ActorIds { get; set; } = Array.Empty(); 13 | } -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/Model/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MediaLibrary.Shared.Model; 4 | 5 | public class PersonModel : IModel 6 | { 7 | public int Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } = string.Empty; 11 | public DateTime BirthDay { get; set; } 12 | public string BirthPlace { get; set; } = string.Empty; 13 | public string Biography { get; set; } = string.Empty; 14 | public int[] MoviesIds { get; set; } = Array.Empty(); 15 | } 16 | -------------------------------------------------------------------------------- /ch6/01_demo/MediaLibrary/MediaLibrary/Shared/SharedMapperProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Google.Protobuf.WellKnownTypes; 3 | 4 | namespace MediaLibrary.Shared; 5 | public class SharedMapperProfile : Profile 6 | { 7 | public SharedMapperProfile() 8 | { 9 | CreateMap() 10 | .ConvertUsing(x => Timestamp.FromDateTime(DateTime.SpecifyKind(x, DateTimeKind.Utc))); 11 | 12 | CreateMap() 13 | .ConvertUsing(x => x.ToDateTime()); 14 | 15 | CreateMap().ReverseMap(); 16 | CreateMap(); 17 | CreateMap() 18 | .ForMember(x => x.Categories, map => map.MapFrom(s => s.Categories)); 19 | } 20 | } 21 | --------------------------------------------------------------------------------