├── .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 |
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 |
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 |
12 |
13 |
14 | @Body
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch2/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
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 |
12 |
13 |
14 | @Body
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch3/demo/MediaLibrary/MediaLibrary/Client/Shared/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
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 | @column.Name |
10 | }
11 | |
12 |
13 |
14 |
15 |
16 |
17 | @foreach (var data in item.Values)
18 | {
19 |
20 | @if (data.Value is null)
21 | {
22 | ---
23 | }
24 | else
25 | {
26 | @data.Value
27 | }
28 | |
29 | }
30 |
31 |
32 |
35 | |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/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 |
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 |
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 | @column.Name |
10 | }
11 | |
12 |
13 |
14 |
15 |
16 |
17 | @foreach (var data in item.Values)
18 | {
19 |
20 | @if (data.Value is null)
21 | {
22 | ---
23 | }
24 | else
25 | {
26 | @data.Value
27 | }
28 | |
29 | }
30 |
31 |
32 |
35 | |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ch5/01_demo_implementation/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
7 |
8 |
9 |
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 |
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 | @column.Name |
10 | }
11 | |
12 |
13 |
14 |
15 |
16 |
17 | @foreach (var data in item.Values)
18 | {
19 |
20 | @if (data.Value is null)
21 | {
22 | ---
23 | }
24 | else
25 | {
26 | @data.Value
27 | }
28 | |
29 | }
30 |
31 |
32 |
35 | |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/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 | @column.Name |
12 | }
13 | |
14 |
15 |
16 |
17 |
18 |
19 | @foreach (var data in item.Values)
20 | {
21 |
22 | @if (data.Value is null)
23 | {
24 | ---
25 | }
26 | else
27 | {
28 | @data.Value
29 | }
30 | |
31 | }
32 |
33 |
34 |
37 | |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ch5/02_demo_blazor/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
7 |
8 |
9 |
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 |
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 | @column.Name |
10 | }
11 | |
12 |
13 |
14 |
15 |
16 |
17 | @foreach (var data in item.Values)
18 | {
19 |
20 | @if (data.Value is null)
21 | {
22 | ---
23 | }
24 | else
25 | {
26 | @data.Value
27 | }
28 | |
29 | }
30 |
31 |
32 |
35 | |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/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 | @column.Name |
12 | }
13 | |
14 |
15 |
16 |
17 |
18 |
19 | @foreach (var data in item.Values)
20 | {
21 |
22 | @if (data.Value is null)
23 | {
24 | ---
25 | }
26 | else
27 | {
28 | @data.Value
29 | }
30 | |
31 | }
32 |
33 |
34 |
37 | |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ch6/01_demo/MediaLibrary/MediaLibrary/Client/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
7 |
8 |
9 |
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 |
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 |
--------------------------------------------------------------------------------