├── .gitattributes ├── .gitignore ├── 1-In Memory Only ├── RepositoryDemo.sln └── RepositoryDemo │ ├── RepositoryDemo.Client │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ └── CustomerRepository.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ └── InMemoryCustomersController.cs │ ├── Data │ └── MemoryRepository.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ └── favicon.png ├── 2-Added EF Repository ├── RepositoryDemo.sln └── RepositoryDemo │ ├── RepositoryDemo.Client │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ └── CustomerRepository.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ ├── EFCustomersController.cs │ └── InMemoryCustomersController.cs │ ├── Data │ ├── EFRepository.cs │ ├── MemoryRepository.cs │ └── RepositoryDemoContext.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ └── favicon.png ├── 3-Added Dapper Repository ├── RepositoryDemo.sln └── RepositoryDemo │ ├── RepositoryDemo.Client │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ └── CustomerRepository.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ ├── DapperCustomersController.cs │ ├── EFCustomersController.cs │ └── InMemoryCustomersController.cs │ ├── Data │ ├── DapperRepository.cs │ ├── DapperSqlHelper.cs │ ├── EFRepository.cs │ ├── MemoryRepository.cs │ └── RepositoryDemoContext.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ └── favicon.png ├── 4-Added IndexedDB Repository ├── RepositoryDemo.sln └── RepositoryDemo │ ├── RepositoryDemo.Client │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ ├── CustomerIndexedDBRepository.cs │ │ ├── CustomerRepository.cs │ │ └── IndexedDBRepository.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ ├── DapperCustomersController.cs │ ├── EFCustomersController.cs │ └── InMemoryCustomersController.cs │ ├── Data │ ├── DapperRepository.cs │ ├── DapperSqlHelper.cs │ ├── EFRepository.cs │ ├── MemoryRepository.cs │ └── RepositoryDemoContext.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ └── favicon.png ├── 5-Added IndexedDB Sync Repository ├── RepositoryDemo.sln └── RepositoryDemo │ ├── RepositoryDemo.Client │ ├── ConnectivityIndicator.razor │ ├── Globals.cs │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ ├── Syncdemo.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ ├── CustomerIndexedDBRepository.cs │ │ ├── CustomerIndexedDBSyncRepository.cs │ │ ├── CustomerRepository.cs │ │ ├── IndexedDBRepository.cs │ │ ├── IndexedDBSyncRepository.cs │ │ ├── LocalTransaction.cs │ │ ├── LocalTransactionTypes.cs │ │ ├── OnlineOfflineKey.cs │ │ └── OnlineStatusEventArgs.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── images │ │ ├── internet-off.png │ │ └── internet-on.png │ └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ ├── DapperCustomersController.cs │ ├── EFCustomersController.cs │ └── InMemoryCustomersController.cs │ ├── Data │ ├── DapperRepository.cs │ ├── DapperSqlHelper.cs │ ├── EFRepository.cs │ ├── MemoryRepository.cs │ └── RepositoryDemoContext.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ ├── favicon.png │ └── js │ └── connectivity.js ├── README.md ├── RepositoryDemo.sln ├── RepositoryDemo ├── RepositoryDemo.Client │ ├── ConnectivityIndicator.razor │ ├── Globals.cs │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Models │ │ └── Customer.cs │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ ├── Syncdemo.razor │ │ └── Weather.razor │ ├── Program.cs │ ├── RepositoryDemo.Client.csproj │ ├── Routes.razor │ ├── Services │ │ ├── APIRepository.cs │ │ ├── CustomerIndexedDBRepository.cs │ │ ├── CustomerIndexedDBSyncRepository.cs │ │ ├── CustomerRepository.cs │ │ ├── DataChangedEventArgs.cs │ │ ├── IndexedDBRepository.cs │ │ ├── IndexedDBSyncRepository.cs │ │ ├── LocalTransaction.cs │ │ ├── LocalTransactionTypes.cs │ │ ├── OnlineOfflineKey.cs │ │ └── OnlineStatusEventArgs.cs │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── images │ │ ├── internet-off.png │ │ └── internet-on.png └── RepositoryDemo │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Controllers │ ├── DapperCustomersController.cs │ ├── EFCustomersController.cs │ └── InMemoryCustomersController.cs │ ├── Data │ ├── DapperRepository.cs │ ├── DapperSqlHelper.cs │ ├── EFRepository.cs │ ├── MemoryRepository.cs │ └── RepositoryDemoContext.cs │ ├── DataSyncHub.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── RepositoryDemo.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ ├── favicon.png │ └── js │ └── connectivity.js └── images ├── image-20210514130755412.png ├── image-20210514130900651.png ├── image-20210514130923188.png ├── image-20210514131058298.png ├── image-20210514131219347.png ├── image-20210514131241975.png ├── image-20210514131305800.png ├── image-20210514131416003.png ├── image-20220322175149382.png ├── image-20220323174510706.png ├── image-20220323174917490.png ├── image-20220323175142470.png ├── image-20220323191200976.png ├── image-20220410180617860.png ├── image-20220609211836651.png ├── image-20220609212248231.png ├── image-20220609212350230.png ├── image-20240423085451476.png ├── image-20240423093155261.png ├── image-20240423094028562.png ├── image-20240423094115654.png ├── image-20240423095032139.png ├── image-20240423114717798.png ├── image-20240423115656651.png ├── image-20240423120105891.png └── image-20240423120221785.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill-nav-menu { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill-nav-menu { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested-nav-menu { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep .nav-link { 63 | color: #d7d7d7; 64 | background: none; 65 | border: none; 66 | border-radius: 4px; 67 | height: 3rem; 68 | display: flex; 69 | align-items: center; 70 | line-height: 3rem; 71 | width: 100%; 72 | } 73 | 74 | .nav-item ::deep a.active { 75 | background-color: rgba(255,255,255,0.37); 76 | color: white; 77 | } 78 | 79 | .nav-item ::deep .nav-link:hover { 80 | background-color: rgba(255,255,255,0.1); 81 | color: white; 82 | } 83 | 84 | .nav-scrollable { 85 | display: none; 86 | } 87 | 88 | .navbar-toggler:checked ~ .nav-scrollable { 89 | display: block; 90 | } 91 | 92 | @media (min-width: 641px) { 93 | .navbar-toggler { 94 | display: none; 95 | } 96 | 97 | .nav-scrollable { 98 | /* Never collapse the sidebar for wide screens */ 99 | display: block; 100 | 101 | /* Allow sidebar to scroll for tall menus */ 102 | height: calc(100vh - 3.5rem); 103 | overflow-y: auto; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Models; 2 | 3 | public class Customer 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = ""; 7 | public string Email { get; set; } = ""; 8 | } 9 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Net.Http.Json; 2 | global using Newtonsoft.Json; 3 | global using System.Net; 4 | global using System.Linq.Expressions; 5 | global using AvnRepository; 6 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 7 | using RepositoryDemo.Client.Services; 8 | 9 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 10 | 11 | builder.Services.AddScoped(sp => new HttpClient 12 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 13 | 14 | builder.Services.AddScoped(); 15 | 16 | 17 | await builder.Build().RunAsync(); 18 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using RepositoryDemo.Client.Models; 2 | namespace RepositoryDemo.Client.Services; 3 | 4 | public class CustomerRepository : APIRepository 5 | { 6 | HttpClient http; 7 | 8 | static string controllerName = "inmemorycustomers"; 9 | 10 | public CustomerRepository(HttpClient _http) 11 | : base(_http, controllerName, "Id") 12 | { 13 | http = _http; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Linq.Expressions; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | using RepositoryDemo.Client.Models; 5 | using RepositoryDemo.Client.Pages; 6 | using RepositoryDemo.Components; 7 | using RepositoryDemo.Data; 8 | 9 | var builder = WebApplication.CreateBuilder(args); 10 | 11 | // Add services to the container. 12 | builder.Services.AddRazorComponents() 13 | .AddInteractiveWebAssemblyComponents(); 14 | 15 | builder.Services.AddSingleton>(x => 16 | new MemoryRepository("Id")); 17 | 18 | builder.Services.AddControllers(); 19 | 20 | var app = builder.Build(); 21 | 22 | app.MapControllers(); 23 | 24 | // Configure the HTTP request pipeline. 25 | if (app.Environment.IsDevelopment()) 26 | { 27 | app.UseWebAssemblyDebugging(); 28 | } 29 | else 30 | { 31 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 32 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 33 | app.UseHsts(); 34 | } 35 | 36 | app.UseHttpsRedirection(); 37 | 38 | app.UseStaticFiles(); 39 | app.UseAntiforgery(); 40 | 41 | app.MapRazorComponents() 42 | .AddInteractiveWebAssemblyRenderMode() 43 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 44 | 45 | app.Run(); 46 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7147;http://localhost:5100", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /1-In Memory Only/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/1-In Memory Only/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill-nav-menu { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill-nav-menu { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested-nav-menu { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep .nav-link { 63 | color: #d7d7d7; 64 | background: none; 65 | border: none; 66 | border-radius: 4px; 67 | height: 3rem; 68 | display: flex; 69 | align-items: center; 70 | line-height: 3rem; 71 | width: 100%; 72 | } 73 | 74 | .nav-item ::deep a.active { 75 | background-color: rgba(255,255,255,0.37); 76 | color: white; 77 | } 78 | 79 | .nav-item ::deep .nav-link:hover { 80 | background-color: rgba(255,255,255,0.1); 81 | color: white; 82 | } 83 | 84 | .nav-scrollable { 85 | display: none; 86 | } 87 | 88 | .navbar-toggler:checked ~ .nav-scrollable { 89 | display: block; 90 | } 91 | 92 | @media (min-width: 641px) { 93 | .navbar-toggler { 94 | display: none; 95 | } 96 | 97 | .nav-scrollable { 98 | /* Never collapse the sidebar for wide screens */ 99 | display: block; 100 | 101 | /* Allow sidebar to scroll for tall menus */ 102 | height: calc(100vh - 3.5rem); 103 | overflow-y: auto; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Models; 2 | 3 | public class Customer 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } = ""; 7 | public string Email { get; set; } = ""; 8 | } 9 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Net.Http.Json; 2 | global using Newtonsoft.Json; 3 | global using System.Net; 4 | global using System.Linq.Expressions; 5 | global using AvnRepository; 6 | global using RepositoryDemo.Client.Models; 7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 8 | using RepositoryDemo.Client.Services; 9 | 10 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 11 | 12 | builder.Services.AddScoped(sp => new HttpClient 13 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 14 | 15 | builder.Services.AddScoped(); 16 | 17 | 18 | await builder.Build().RunAsync(); 19 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using RepositoryDemo.Client.Models; 2 | namespace RepositoryDemo.Client.Services; 3 | 4 | public class CustomerRepository : APIRepository 5 | { 6 | HttpClient http; 7 | 8 | // swap out the controller name 9 | //static string controllerName = "inmemorycustomers"; 10 | static string controllerName = "efcustomers"; 11 | 12 | public CustomerRepository(HttpClient _http) 13 | : base(_http, controllerName, "Id") 14 | { 15 | http = _http; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Data/EFRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class EFRepository : IRepository 4 | where TEntity : class 5 | where TDataContext : DbContext 6 | { 7 | protected readonly TDataContext context; 8 | internal DbSet dbSet; 9 | 10 | public EFRepository(TDataContext dataContext) 11 | { 12 | context = dataContext; 13 | context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 14 | dbSet = context.Set(); 15 | } 16 | public async Task> GetAllAsync() 17 | { 18 | return await Task.FromResult(dbSet); 19 | } 20 | 21 | public async Task GetByIdAsync(object Id) 22 | { 23 | return await dbSet.FindAsync(Id); 24 | } 25 | 26 | public async Task> GetAsync(QueryFilter Filter) 27 | { 28 | var allitems = (await GetAllAsync()).ToList(); 29 | return Filter.GetFilteredList(allitems); 30 | } 31 | 32 | public async Task InsertAsync(TEntity entity) 33 | { 34 | await dbSet.AddAsync(entity); 35 | await context.SaveChangesAsync(); 36 | return entity; 37 | } 38 | 39 | public async Task UpdateAsync(TEntity entityToUpdate) 40 | { 41 | var dbSet = context.Set(); 42 | dbSet.Attach(entityToUpdate); 43 | context.Entry(entityToUpdate).State = EntityState.Modified; 44 | await context.SaveChangesAsync(); 45 | return entityToUpdate; 46 | } 47 | 48 | public async Task DeleteAsync(TEntity entityToDelete) 49 | { 50 | if (context.Entry(entityToDelete).State == EntityState.Detached) 51 | { 52 | dbSet.Attach(entityToDelete); 53 | } 54 | dbSet.Remove(entityToDelete); 55 | return await context.SaveChangesAsync() >= 1; 56 | } 57 | 58 | public async Task DeleteByIdAsync(object id) 59 | { 60 | TEntity entityToDelete = await dbSet.FindAsync(id); 61 | return await DeleteAsync(entityToDelete); 62 | } 63 | 64 | public async Task DeleteAllAsync() 65 | { 66 | await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE Customer"); 67 | } 68 | } -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Data/RepositoryDemoContext.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public partial class RepositoryDemoContext : DbContext 4 | { 5 | public RepositoryDemoContext() 6 | { 7 | } 8 | 9 | public RepositoryDemoContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public virtual DbSet Customers { get; set; } 15 | 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 17 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. 18 | => optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=RepositoryDemo;Trusted_Connection=True;"); 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.HasKey(e => e.Id).HasName("PK__Customer__3214EC07A92CECC5"); 25 | 26 | entity.ToTable("Customer"); 27 | 28 | entity.Property(e => e.Email).HasMaxLength(50); 29 | entity.Property(e => e.Name).HasMaxLength(50); 30 | }); 31 | 32 | OnModelCreatingPartial(modelBuilder); 33 | } 34 | 35 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 36 | } 37 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Linq.Expressions; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using RepositoryDemo.Client.Models; 6 | 7 | using RepositoryDemo.Client.Pages; 8 | using RepositoryDemo.Components; 9 | using RepositoryDemo.Data; 10 | 11 | var builder = WebApplication.CreateBuilder(args); 12 | 13 | // Add services to the container. 14 | builder.Services.AddRazorComponents() 15 | .AddInteractiveWebAssemblyComponents(); 16 | 17 | builder.Services.AddSingleton>(x => 18 | new MemoryRepository("Id")); 19 | 20 | builder.Services.AddTransient(); 21 | builder.Services.AddTransient>(); 22 | 23 | builder.Services.AddControllers(); 24 | 25 | var app = builder.Build(); 26 | 27 | app.MapControllers(); 28 | 29 | // Configure the HTTP request pipeline. 30 | if (app.Environment.IsDevelopment()) 31 | { 32 | app.UseWebAssemblyDebugging(); 33 | } 34 | else 35 | { 36 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 37 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 38 | app.UseHsts(); 39 | } 40 | 41 | app.UseHttpsRedirection(); 42 | 43 | app.UseStaticFiles(); 44 | app.UseAntiforgery(); 45 | 46 | app.MapRazorComponents() 47 | .AddInteractiveWebAssemblyRenderMode() 48 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 49 | 50 | app.Run(); 51 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7147;http://localhost:5100", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /2-Added EF Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/2-Added EF Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.ComponentModel.DataAnnotations; 3 | namespace RepositoryDemo.Client.Models; 4 | 5 | [Table("Customer")] 6 | public class Customer 7 | { 8 | [Key] 9 | public int Id { get; set; } 10 | public string Name { get; set; } = ""; 11 | public string Email { get; set; } = ""; 12 | } 13 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Net.Http.Json; 2 | global using Newtonsoft.Json; 3 | global using System.Net; 4 | global using System.Linq.Expressions; 5 | global using AvnRepository; 6 | global using RepositoryDemo.Client.Models; 7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 8 | using RepositoryDemo.Client.Services; 9 | 10 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 11 | 12 | builder.Services.AddScoped(sp => new HttpClient 13 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 14 | 15 | builder.Services.AddScoped(); 16 | 17 | 18 | await builder.Build().RunAsync(); 19 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerRepository : APIRepository 4 | { 5 | HttpClient http; 6 | 7 | // swap out the controller name 8 | //static string controllerName = "inmemorycustomers"; 9 | //static string controllerName = "efcustomers"; 10 | static string controllerName = "dappercustomers"; 11 | 12 | public CustomerRepository(HttpClient _http) 13 | : base(_http, controllerName, "Id") 14 | { 15 | http = _http; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Data/DapperSqlHelper.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class DapperSqlHelper 4 | { 5 | public static string GetDapperInsertStatement(object Entity, string TableName) 6 | { 7 | // let's get the SQL string started. 8 | string sql = $"insert into {TableName} ("; 9 | 10 | // Get the type, and the list of public properties 11 | var EntityType = Entity.GetType(); 12 | var Properties = EntityType.GetProperties(); 13 | 14 | foreach (var property in Properties) 15 | { 16 | // Is this property nullable? 17 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 18 | { 19 | // yes. get the value. 20 | var value = property.GetValue(Entity); 21 | // is the value null? 22 | if (value != null) 23 | // only add if the value is not null 24 | sql += $"{property.Name}, "; 25 | } 26 | // is this property virtual (like Customer.Invoices)? 27 | else if (property.GetGetMethod().IsVirtual == false) 28 | { 29 | // not virtual. Include 30 | sql += $"{property.Name}, "; 31 | } 32 | } 33 | 34 | // At this point there is a trailing ", " that we need to remove 35 | sql = sql.Substring(0, sql.Length - 2); 36 | 37 | // add the start of the values clause 38 | sql += ") values ("; 39 | 40 | // Once more through the properties 41 | foreach (var property in Properties) 42 | { 43 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 44 | { 45 | var value = property.GetValue(Entity); 46 | if (value != null) 47 | // inserts in Dapper are paramterized, so at least 48 | // we don't have to figure out data types, quotes, etc. 49 | sql += $"@{property.Name}, "; 50 | } 51 | else if (property.GetGetMethod().IsVirtual == false) 52 | { 53 | sql += $"@{property.Name}, "; 54 | } 55 | } 56 | 57 | // again, remove the trailing ", " and finish with a closed paren 58 | sql = sql.Substring(0, sql.Length - 2) + ")"; 59 | 60 | // we're outta here! 61 | return sql; 62 | } 63 | } -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Data/EFRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class EFRepository : IRepository 4 | where TEntity : class 5 | where TDataContext : DbContext 6 | { 7 | protected readonly TDataContext context; 8 | internal DbSet dbSet; 9 | 10 | public EFRepository(TDataContext dataContext) 11 | { 12 | context = dataContext; 13 | context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 14 | dbSet = context.Set(); 15 | } 16 | public async Task> GetAllAsync() 17 | { 18 | return await Task.FromResult(dbSet); 19 | } 20 | 21 | public async Task GetByIdAsync(object Id) 22 | { 23 | return await dbSet.FindAsync(Id); 24 | } 25 | 26 | public async Task> GetAsync(QueryFilter Filter) 27 | { 28 | var allitems = (await GetAllAsync()).ToList(); 29 | return Filter.GetFilteredList(allitems); 30 | } 31 | 32 | public async Task InsertAsync(TEntity entity) 33 | { 34 | await dbSet.AddAsync(entity); 35 | await context.SaveChangesAsync(); 36 | return entity; 37 | } 38 | 39 | public async Task UpdateAsync(TEntity entityToUpdate) 40 | { 41 | var dbSet = context.Set(); 42 | dbSet.Attach(entityToUpdate); 43 | context.Entry(entityToUpdate).State = EntityState.Modified; 44 | await context.SaveChangesAsync(); 45 | return entityToUpdate; 46 | } 47 | 48 | public async Task DeleteAsync(TEntity entityToDelete) 49 | { 50 | if (context.Entry(entityToDelete).State == EntityState.Detached) 51 | { 52 | dbSet.Attach(entityToDelete); 53 | } 54 | dbSet.Remove(entityToDelete); 55 | return await context.SaveChangesAsync() >= 1; 56 | } 57 | 58 | public async Task DeleteByIdAsync(object id) 59 | { 60 | TEntity entityToDelete = await dbSet.FindAsync(id); 61 | return await DeleteAsync(entityToDelete); 62 | } 63 | 64 | public async Task DeleteAllAsync() 65 | { 66 | await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE Customer"); 67 | } 68 | } -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Data/RepositoryDemoContext.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public partial class RepositoryDemoContext : DbContext 4 | { 5 | public RepositoryDemoContext() 6 | { 7 | } 8 | 9 | public RepositoryDemoContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public virtual DbSet Customers { get; set; } 15 | 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 17 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. 18 | => optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=RepositoryDemo;Trusted_Connection=True;"); 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.HasKey(e => e.Id).HasName("PK__Customer__3214EC07A92CECC5"); 25 | 26 | entity.ToTable("Customer"); 27 | 28 | entity.Property(e => e.Email).HasMaxLength(50); 29 | entity.Property(e => e.Name).HasMaxLength(50); 30 | }); 31 | 32 | OnModelCreatingPartial(modelBuilder); 33 | } 34 | 35 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 36 | } 37 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Linq.Expressions; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using RepositoryDemo.Client.Models; 6 | global using System.Data.SqlClient; 7 | global using Dapper; 8 | global using Dapper.Contrib.Extensions; 9 | global using System.Data; 10 | using RepositoryDemo.Client.Pages; 11 | using RepositoryDemo.Components; 12 | using RepositoryDemo.Data; 13 | 14 | var builder = WebApplication.CreateBuilder(args); 15 | 16 | // Add services to the container. 17 | builder.Services.AddRazorComponents() 18 | .AddInteractiveWebAssemblyComponents(); 19 | 20 | builder.Services.AddSingleton>(x => 21 | new MemoryRepository("Id")); 22 | 23 | builder.Services.AddTransient(); 24 | builder.Services.AddTransient>(); 25 | 26 | builder.Services.AddTransient>(s => 27 | new DapperRepository( 28 | builder.Configuration.GetConnectionString("RepositoryDemoConnectionString"))); 29 | 30 | builder.Services.AddControllers(); 31 | 32 | var app = builder.Build(); 33 | 34 | app.MapControllers(); 35 | 36 | // Configure the HTTP request pipeline. 37 | if (app.Environment.IsDevelopment()) 38 | { 39 | app.UseWebAssemblyDebugging(); 40 | } 41 | else 42 | { 43 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 44 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 45 | app.UseHsts(); 46 | } 47 | 48 | app.UseHttpsRedirection(); 49 | 50 | app.UseStaticFiles(); 51 | app.UseAntiforgery(); 52 | 53 | app.MapRazorComponents() 54 | .AddInteractiveWebAssemblyRenderMode() 55 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 56 | 57 | app.Run(); 58 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7147;http://localhost:5100", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "RepositoryDemoConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RepositoryDemo;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" 11 | } 12 | } -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/3-Added Dapper Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.ComponentModel.DataAnnotations; 3 | namespace RepositoryDemo.Client.Models; 4 | 5 | [Table("Customer")] 6 | public class Customer 7 | { 8 | [Key] 9 | public int Id { get; set; } 10 | public string Name { get; set; } = ""; 11 | public string Email { get; set; } = ""; 12 | } 13 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using BlazorDB; 2 | global using System.Net.Http.Json; 3 | global using Newtonsoft.Json; 4 | global using System.Net; 5 | global using System.Linq.Expressions; 6 | global using AvnRepository; 7 | global using RepositoryDemo.Client.Models; 8 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 9 | using RepositoryDemo.Client.Services; 10 | 11 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 12 | 13 | builder.Services.AddScoped(sp => new HttpClient 14 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 15 | 16 | builder.Services.AddScoped(); 17 | 18 | builder.Services.AddBlazorDB(options => 19 | { 20 | options.Name = "RepositoryDemo"; 21 | options.Version = 1; 22 | 23 | // List all your entities here, but as StoreSchema objects 24 | options.StoreSchemas = new List() 25 | { 26 | new StoreSchema() 27 | { 28 | Name = "Customer", // Name of entity 29 | PrimaryKey = "Id", // Primary Key of entity 30 | PrimaryKeyAuto = true, // Whether or not the Primary key is generated 31 | Indexes = new List { "Id" } 32 | } 33 | }; 34 | }); 35 | 36 | builder.Services.AddScoped(); 37 | 38 | await builder.Build().RunAsync(); 39 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerIndexedDBRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerIndexedDBRepository : IndexedDBRepository 4 | { 5 | public CustomerIndexedDBRepository(IBlazorDbFactory dbFactory) 6 | : base("RepositoryDemo", "Id", true, dbFactory) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerRepository : APIRepository 4 | { 5 | HttpClient http; 6 | 7 | // swap out the controller name 8 | //static string controllerName = "inmemorycustomers"; 9 | //static string controllerName = "efcustomers"; 10 | static string controllerName = "dappercustomers"; 11 | 12 | public CustomerRepository(HttpClient _http) 13 | : base(_http, controllerName, "Id") 14 | { 15 | http = _http; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository 13 | @using BlazorDB -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Data/DapperSqlHelper.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class DapperSqlHelper 4 | { 5 | public static string GetDapperInsertStatement(object Entity, string TableName) 6 | { 7 | // let's get the SQL string started. 8 | string sql = $"insert into {TableName} ("; 9 | 10 | // Get the type, and the list of public properties 11 | var EntityType = Entity.GetType(); 12 | var Properties = EntityType.GetProperties(); 13 | 14 | foreach (var property in Properties) 15 | { 16 | // Is this property nullable? 17 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 18 | { 19 | // yes. get the value. 20 | var value = property.GetValue(Entity); 21 | // is the value null? 22 | if (value != null) 23 | // only add if the value is not null 24 | sql += $"{property.Name}, "; 25 | } 26 | // is this property virtual (like Customer.Invoices)? 27 | else if (property.GetGetMethod().IsVirtual == false) 28 | { 29 | // not virtual. Include 30 | sql += $"{property.Name}, "; 31 | } 32 | } 33 | 34 | // At this point there is a trailing ", " that we need to remove 35 | sql = sql.Substring(0, sql.Length - 2); 36 | 37 | // add the start of the values clause 38 | sql += ") values ("; 39 | 40 | // Once more through the properties 41 | foreach (var property in Properties) 42 | { 43 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 44 | { 45 | var value = property.GetValue(Entity); 46 | if (value != null) 47 | // inserts in Dapper are paramterized, so at least 48 | // we don't have to figure out data types, quotes, etc. 49 | sql += $"@{property.Name}, "; 50 | } 51 | else if (property.GetGetMethod().IsVirtual == false) 52 | { 53 | sql += $"@{property.Name}, "; 54 | } 55 | } 56 | 57 | // again, remove the trailing ", " and finish with a closed paren 58 | sql = sql.Substring(0, sql.Length - 2) + ")"; 59 | 60 | // we're outta here! 61 | return sql; 62 | } 63 | } -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Data/EFRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class EFRepository : IRepository 4 | where TEntity : class 5 | where TDataContext : DbContext 6 | { 7 | protected readonly TDataContext context; 8 | internal DbSet dbSet; 9 | 10 | public EFRepository(TDataContext dataContext) 11 | { 12 | context = dataContext; 13 | context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 14 | dbSet = context.Set(); 15 | } 16 | public async Task> GetAllAsync() 17 | { 18 | return await Task.FromResult(dbSet); 19 | } 20 | 21 | public async Task GetByIdAsync(object Id) 22 | { 23 | return await dbSet.FindAsync(Id); 24 | } 25 | 26 | public async Task> GetAsync(QueryFilter Filter) 27 | { 28 | var allitems = (await GetAllAsync()).ToList(); 29 | return Filter.GetFilteredList(allitems); 30 | } 31 | 32 | public async Task InsertAsync(TEntity entity) 33 | { 34 | await dbSet.AddAsync(entity); 35 | await context.SaveChangesAsync(); 36 | return entity; 37 | } 38 | 39 | public async Task UpdateAsync(TEntity entityToUpdate) 40 | { 41 | var dbSet = context.Set(); 42 | dbSet.Attach(entityToUpdate); 43 | context.Entry(entityToUpdate).State = EntityState.Modified; 44 | await context.SaveChangesAsync(); 45 | return entityToUpdate; 46 | } 47 | 48 | public async Task DeleteAsync(TEntity entityToDelete) 49 | { 50 | if (context.Entry(entityToDelete).State == EntityState.Detached) 51 | { 52 | dbSet.Attach(entityToDelete); 53 | } 54 | dbSet.Remove(entityToDelete); 55 | return await context.SaveChangesAsync() >= 1; 56 | } 57 | 58 | public async Task DeleteByIdAsync(object id) 59 | { 60 | TEntity entityToDelete = await dbSet.FindAsync(id); 61 | return await DeleteAsync(entityToDelete); 62 | } 63 | 64 | public async Task DeleteAllAsync() 65 | { 66 | await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE Customer"); 67 | } 68 | } -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Data/RepositoryDemoContext.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public partial class RepositoryDemoContext : DbContext 4 | { 5 | public RepositoryDemoContext() 6 | { 7 | } 8 | 9 | public RepositoryDemoContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public virtual DbSet Customers { get; set; } 15 | 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 17 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. 18 | => optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=RepositoryDemo;Trusted_Connection=True;"); 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.HasKey(e => e.Id).HasName("PK__Customer__3214EC07A92CECC5"); 25 | 26 | entity.ToTable("Customer"); 27 | 28 | entity.Property(e => e.Email).HasMaxLength(50); 29 | entity.Property(e => e.Name).HasMaxLength(50); 30 | }); 31 | 32 | OnModelCreatingPartial(modelBuilder); 33 | } 34 | 35 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 36 | } 37 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Linq.Expressions; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using RepositoryDemo.Client.Models; 6 | global using System.Data.SqlClient; 7 | global using Dapper; 8 | global using Dapper.Contrib.Extensions; 9 | global using System.Data; 10 | using RepositoryDemo.Client.Pages; 11 | using RepositoryDemo.Components; 12 | using RepositoryDemo.Data; 13 | 14 | var builder = WebApplication.CreateBuilder(args); 15 | 16 | // Add services to the container. 17 | builder.Services.AddRazorComponents() 18 | .AddInteractiveWebAssemblyComponents(); 19 | 20 | builder.Services.AddSingleton>(x => 21 | new MemoryRepository("Id")); 22 | 23 | builder.Services.AddTransient(); 24 | builder.Services.AddTransient>(); 25 | 26 | builder.Services.AddTransient>(s => 27 | new DapperRepository( 28 | builder.Configuration.GetConnectionString("RepositoryDemoConnectionString"))); 29 | 30 | builder.Services.AddControllers(); 31 | 32 | var app = builder.Build(); 33 | 34 | app.MapControllers(); 35 | 36 | // Configure the HTTP request pipeline. 37 | if (app.Environment.IsDevelopment()) 38 | { 39 | app.UseWebAssemblyDebugging(); 40 | } 41 | else 42 | { 43 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 44 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 45 | app.UseHsts(); 46 | } 47 | 48 | app.UseHttpsRedirection(); 49 | 50 | app.UseStaticFiles(); 51 | app.UseAntiforgery(); 52 | 53 | app.MapRazorComponents() 54 | .AddInteractiveWebAssemblyRenderMode() 55 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 56 | 57 | app.Run(); 58 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7147;http://localhost:5100", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "RepositoryDemoConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RepositoryDemo;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" 11 | } 12 | } -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/4-Added IndexedDB Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/ConnectivityIndicator.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime _jsRuntime; 2 | @implements IAsyncDisposable 3 | 4 | @if (IsOnline) 5 | { 6 | @ShowOnline 7 | } 8 | else 9 | { 10 | @ShowOffline 11 | } 12 | 13 | @code { 14 | [Parameter] 15 | public RenderFragment ShowOnline { get; set; } 16 | 17 | [Parameter] 18 | public RenderFragment ShowOffline { get; set; } 19 | 20 | public bool IsOnline { get; set; } 21 | 22 | [JSInvokable("ConnectivityChanged")] 23 | public void OnConnectivityChanged(bool isOnline) 24 | { 25 | if (IsOnline != isOnline) 26 | { 27 | IsOnline = isOnline; 28 | } 29 | 30 | StateHasChanged(); 31 | } 32 | 33 | protected override async Task OnInitializedAsync() 34 | { 35 | await base.OnInitializedAsync(); 36 | 37 | await _jsRuntime.InvokeVoidAsync("connectivity.initialize", 38 | DotNetObjectReference.Create(this)); 39 | } 40 | 41 | public async ValueTask DisposeAsync() 42 | { 43 | await _jsRuntime.InvokeVoidAsync("connectivity.`dispose`"); 44 | } 45 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client; 2 | 3 | public static class Globals 4 | { 5 | public const string LocalTransactionsSuffix = "_transactions"; 6 | public const string KeysSuffix = "_keys"; 7 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | 11 | 12 | Online 15 | 16 | 17 | Offline 20 | 21 | 22 | About 23 |
24 | 25 |
26 | @Body 27 |
28 |
29 |
30 | 31 |
32 | An unhandled error has occurred. 33 | Reload 34 | 🗙 35 |
36 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.ComponentModel.DataAnnotations; 3 | namespace RepositoryDemo.Client.Models; 4 | 5 | [Table("Customer")] 6 | public class Customer 7 | { 8 | [Key] 9 | public int Id { get; set; } 10 | public string Name { get; set; } = ""; 11 | public string Email { get; set; } = ""; 12 | } 13 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using BlazorDB; 2 | global using System.Net.Http.Json; 3 | global using Newtonsoft.Json; 4 | global using System.Net; 5 | global using System.Linq.Expressions; 6 | global using AvnRepository; 7 | global using RepositoryDemo.Client.Models; 8 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 9 | using RepositoryDemo.Client.Services; 10 | using RepositoryDemo.Client; 11 | 12 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 13 | 14 | builder.Services.AddScoped(sp => new HttpClient 15 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 16 | 17 | builder.Services.AddScoped(); 18 | 19 | builder.Services.AddBlazorDB(options => 20 | { 21 | options.Name = "RepositoryDemo"; 22 | options.Version = 1; 23 | 24 | // List all your entities here, but as StoreSchema objects 25 | options.StoreSchemas = new List() 26 | { 27 | new StoreSchema() 28 | { 29 | Name = "Customer", // Name of entity 30 | PrimaryKey = "Id", // Primary Key of entity 31 | PrimaryKeyAuto = true, // Whether or not the Primary key is generated 32 | Indexes = new List { "Id" } 33 | }, 34 | new StoreSchema() 35 | { 36 | Name = $"Customer{Globals.LocalTransactionsSuffix}", 37 | PrimaryKey = "Id", 38 | PrimaryKeyAuto = true, 39 | Indexes = new List { "Id" } 40 | }, 41 | new StoreSchema() 42 | { 43 | Name = $"Customer{Globals.KeysSuffix}", 44 | PrimaryKey = "Id", 45 | PrimaryKeyAuto = true, 46 | Indexes = new List { "Id" } 47 | } 48 | }; 49 | }); 50 | 51 | builder.Services.AddScoped(); 52 | builder.Services.AddScoped(); 53 | 54 | await builder.Build().RunAsync(); 55 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerIndexedDBRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerIndexedDBRepository : IndexedDBRepository 4 | { 5 | public CustomerIndexedDBRepository(IBlazorDbFactory dbFactory) 6 | : base("RepositoryDemo", "Id", true, dbFactory) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerIndexedDBSyncRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using RepositoryDemo.Client.Services; 3 | 4 | public class CustomerIndexedDBSyncRepository : IndexedDBSyncRepository 5 | { 6 | public CustomerIndexedDBSyncRepository(IBlazorDbFactory dbFactory, 7 | CustomerRepository customerRepository, IJSRuntime jsRuntime) 8 | : base("RepositoryDemo", "Id", true, dbFactory, customerRepository, jsRuntime) 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerRepository : APIRepository 4 | { 5 | HttpClient http; 6 | 7 | // swap out the controller name 8 | //static string controllerName = "inmemorycustomers"; 9 | //static string controllerName = "efcustomers"; 10 | static string controllerName = "dappercustomers"; 11 | 12 | public CustomerRepository(HttpClient _http) 13 | : base(_http, controllerName, "Id") 14 | { 15 | http = _http; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/LocalTransaction.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class LocalTransaction 4 | { 5 | public TEntity Entity { get; set; } 6 | public LocalTransactionTypes Action { get; set; } 7 | public string ActionName { get; set; } 8 | public object Id { get; set; } 9 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/LocalTransactionTypes.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public enum LocalTransactionTypes 4 | { 5 | Insert = 0, 6 | Update = 1, 7 | Delete = 2, 8 | DeleteAll = 3 9 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/OnlineOfflineKey.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class OnlineOfflineKey 4 | { 5 | public int Id { get; set; } 6 | public object OnlineId { get; set; } 7 | public object LocalId { get; set; } 8 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/Services/OnlineStatusEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class OnlineStatusEventArgs : EventArgs 4 | { 5 | public bool IsOnline { get; set; } 6 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository 13 | @using BlazorDB -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-off.png -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-on.png -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Data/DapperSqlHelper.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class DapperSqlHelper 4 | { 5 | public static string GetDapperInsertStatement(object Entity, string TableName) 6 | { 7 | // let's get the SQL string started. 8 | string sql = $"insert into {TableName} ("; 9 | 10 | // Get the type, and the list of public properties 11 | var EntityType = Entity.GetType(); 12 | var Properties = EntityType.GetProperties(); 13 | 14 | foreach (var property in Properties) 15 | { 16 | // Is this property nullable? 17 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 18 | { 19 | // yes. get the value. 20 | var value = property.GetValue(Entity); 21 | // is the value null? 22 | if (value != null) 23 | // only add if the value is not null 24 | sql += $"{property.Name}, "; 25 | } 26 | // is this property virtual (like Customer.Invoices)? 27 | else if (property.GetGetMethod().IsVirtual == false) 28 | { 29 | // not virtual. Include 30 | sql += $"{property.Name}, "; 31 | } 32 | } 33 | 34 | // At this point there is a trailing ", " that we need to remove 35 | sql = sql.Substring(0, sql.Length - 2); 36 | 37 | // add the start of the values clause 38 | sql += ") values ("; 39 | 40 | // Once more through the properties 41 | foreach (var property in Properties) 42 | { 43 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 44 | { 45 | var value = property.GetValue(Entity); 46 | if (value != null) 47 | // inserts in Dapper are paramterized, so at least 48 | // we don't have to figure out data types, quotes, etc. 49 | sql += $"@{property.Name}, "; 50 | } 51 | else if (property.GetGetMethod().IsVirtual == false) 52 | { 53 | sql += $"@{property.Name}, "; 54 | } 55 | } 56 | 57 | // again, remove the trailing ", " and finish with a closed paren 58 | sql = sql.Substring(0, sql.Length - 2) + ")"; 59 | 60 | // we're outta here! 61 | return sql; 62 | } 63 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Data/EFRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class EFRepository : IRepository 4 | where TEntity : class 5 | where TDataContext : DbContext 6 | { 7 | protected readonly TDataContext context; 8 | internal DbSet dbSet; 9 | 10 | public EFRepository(TDataContext dataContext) 11 | { 12 | context = dataContext; 13 | context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 14 | dbSet = context.Set(); 15 | } 16 | public async Task> GetAllAsync() 17 | { 18 | return await Task.FromResult(dbSet); 19 | } 20 | 21 | public async Task GetByIdAsync(object Id) 22 | { 23 | return await dbSet.FindAsync(Id); 24 | } 25 | 26 | public async Task> GetAsync(QueryFilter Filter) 27 | { 28 | var allitems = (await GetAllAsync()).ToList(); 29 | return Filter.GetFilteredList(allitems); 30 | } 31 | 32 | public async Task InsertAsync(TEntity entity) 33 | { 34 | await dbSet.AddAsync(entity); 35 | await context.SaveChangesAsync(); 36 | return entity; 37 | } 38 | 39 | public async Task UpdateAsync(TEntity entityToUpdate) 40 | { 41 | var dbSet = context.Set(); 42 | dbSet.Attach(entityToUpdate); 43 | context.Entry(entityToUpdate).State = EntityState.Modified; 44 | await context.SaveChangesAsync(); 45 | return entityToUpdate; 46 | } 47 | 48 | public async Task DeleteAsync(TEntity entityToDelete) 49 | { 50 | if (context.Entry(entityToDelete).State == EntityState.Detached) 51 | { 52 | dbSet.Attach(entityToDelete); 53 | } 54 | dbSet.Remove(entityToDelete); 55 | return await context.SaveChangesAsync() >= 1; 56 | } 57 | 58 | public async Task DeleteByIdAsync(object id) 59 | { 60 | TEntity entityToDelete = await dbSet.FindAsync(id); 61 | return await DeleteAsync(entityToDelete); 62 | } 63 | 64 | public async Task DeleteAllAsync() 65 | { 66 | await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE Customer"); 67 | } 68 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Data/RepositoryDemoContext.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public partial class RepositoryDemoContext : DbContext 4 | { 5 | public RepositoryDemoContext() 6 | { 7 | } 8 | 9 | public RepositoryDemoContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public virtual DbSet Customers { get; set; } 15 | 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 17 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. 18 | => optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=RepositoryDemo;Trusted_Connection=True;"); 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.HasKey(e => e.Id).HasName("PK__Customer__3214EC07A92CECC5"); 25 | 26 | entity.ToTable("Customer"); 27 | 28 | entity.Property(e => e.Email).HasMaxLength(50); 29 | entity.Property(e => e.Name).HasMaxLength(50); 30 | }); 31 | 32 | OnModelCreatingPartial(modelBuilder); 33 | } 34 | 35 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 36 | } 37 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using System.Linq.Expressions; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using RepositoryDemo.Client.Models; 6 | global using System.Data.SqlClient; 7 | global using Dapper; 8 | global using Dapper.Contrib.Extensions; 9 | global using System.Data; 10 | using RepositoryDemo.Client.Pages; 11 | using RepositoryDemo.Components; 12 | using RepositoryDemo.Data; 13 | 14 | var builder = WebApplication.CreateBuilder(args); 15 | 16 | // Add services to the container. 17 | builder.Services.AddRazorComponents() 18 | .AddInteractiveWebAssemblyComponents(); 19 | 20 | builder.Services.AddSingleton>(x => 21 | new MemoryRepository("Id")); 22 | 23 | builder.Services.AddTransient(); 24 | builder.Services.AddTransient>(); 25 | 26 | builder.Services.AddTransient>(s => 27 | new DapperRepository( 28 | builder.Configuration.GetConnectionString("RepositoryDemoConnectionString"))); 29 | 30 | builder.Services.AddControllers(); 31 | 32 | var app = builder.Build(); 33 | 34 | app.MapControllers(); 35 | 36 | // Configure the HTTP request pipeline. 37 | if (app.Environment.IsDevelopment()) 38 | { 39 | app.UseWebAssemblyDebugging(); 40 | } 41 | else 42 | { 43 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 44 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 45 | app.UseHsts(); 46 | } 47 | 48 | app.UseHttpsRedirection(); 49 | 50 | app.UseStaticFiles(); 51 | app.UseAntiforgery(); 52 | 53 | app.MapRazorComponents() 54 | .AddInteractiveWebAssemblyRenderMode() 55 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 56 | 57 | app.Run(); 58 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7148;http://localhost:5101", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "RepositoryDemoConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RepositoryDemo;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" 11 | } 12 | } -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /5-Added IndexedDB Sync Repository/RepositoryDemo/RepositoryDemo/wwwroot/js/connectivity.js: -------------------------------------------------------------------------------- 1 | let notify; 2 | 3 | window.connectivity = { 4 | initialize: function (interop) { 5 | 6 | notify = function () { 7 | interop.invokeMethodAsync("ConnectivityChanged", navigator.onLine); 8 | } 9 | 10 | window.addEventListener("online", notify); 11 | window.addEventListener("offline", notify); 12 | 13 | notify(navigator.onLine); 14 | }, 15 | dispose: function () { 16 | 17 | if (handler != null) { 18 | 19 | window.removeEventListener("online", notify); 20 | window.removeEventListener("offline", notify); 21 | } 22 | } 23 | }; -------------------------------------------------------------------------------- /RepositoryDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34728.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryDemo", "RepositoryDemo\RepositoryDemo\RepositoryDemo.csproj", "{258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryDemo.Client", "RepositoryDemo\RepositoryDemo.Client\RepositoryDemo.Client.csproj", "{027BA073-1E9F-4F1E-9CE6-2A4169F36966}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {258B1F79-F9DB-49AC-98B9-BEBC6201BBE3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {027BA073-1E9F-4F1E-9CE6-2A4169F36966}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A947BF9F-79B0-4BF7-84AF-C69C43A92EE4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/ConnectivityIndicator.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime _jsRuntime; 2 | @implements IAsyncDisposable 3 | 4 | @if (IsOnline) 5 | { 6 | @ShowOnline 7 | } 8 | else 9 | { 10 | @ShowOffline 11 | } 12 | 13 | @code { 14 | [Parameter] 15 | public RenderFragment ShowOnline { get; set; } 16 | 17 | [Parameter] 18 | public RenderFragment ShowOffline { get; set; } 19 | 20 | public bool IsOnline { get; set; } 21 | 22 | [JSInvokable("ConnectivityChanged")] 23 | public void OnConnectivityChanged(bool isOnline) 24 | { 25 | if (IsOnline != isOnline) 26 | { 27 | IsOnline = isOnline; 28 | } 29 | 30 | StateHasChanged(); 31 | } 32 | 33 | protected override async Task OnInitializedAsync() 34 | { 35 | await base.OnInitializedAsync(); 36 | 37 | await _jsRuntime.InvokeVoidAsync("connectivity.initialize", 38 | DotNetObjectReference.Create(this)); 39 | } 40 | 41 | public async ValueTask DisposeAsync() 42 | { 43 | await _jsRuntime.InvokeVoidAsync("connectivity.dispose"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client; 2 | 3 | public static class Globals 4 | { 5 | public const string LocalTransactionsSuffix = "_transactions"; 6 | public const string KeysSuffix = "_keys"; 7 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | 11 | 12 | Online 15 | 16 | 17 | Offline 20 | 21 | 22 | About 23 |
24 | 25 |
26 | @Body 27 |
28 |
29 |
30 | 31 |
32 | An unhandled error has occurred. 33 | Reload 34 | 🗙 35 |
36 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill-nav-menu { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill-nav-menu { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested-nav-menu { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep .nav-link { 63 | color: #d7d7d7; 64 | background: none; 65 | border: none; 66 | border-radius: 4px; 67 | height: 3rem; 68 | display: flex; 69 | align-items: center; 70 | line-height: 3rem; 71 | width: 100%; 72 | } 73 | 74 | .nav-item ::deep a.active { 75 | background-color: rgba(255,255,255,0.37); 76 | color: white; 77 | } 78 | 79 | .nav-item ::deep .nav-link:hover { 80 | background-color: rgba(255,255,255,0.1); 81 | color: white; 82 | } 83 | 84 | .nav-scrollable { 85 | display: none; 86 | } 87 | 88 | .navbar-toggler:checked ~ .nav-scrollable { 89 | display: block; 90 | } 91 | 92 | @media (min-width: 641px) { 93 | .navbar-toggler { 94 | display: none; 95 | } 96 | 97 | .nav-scrollable { 98 | /* Never collapse the sidebar for wide screens */ 99 | display: block; 100 | 101 | /* Allow sidebar to scroll for tall menus */ 102 | height: calc(100vh - 3.5rem); 103 | overflow-y: auto; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.ComponentModel.DataAnnotations; 3 | namespace RepositoryDemo.Client.Models; 4 | 5 | [Table("Customer")] 6 | public class Customer 7 | { 8 | [Key] 9 | public int Id { get; set; } 10 | public string Name { get; set; } = ""; 11 | public string Email { get; set; } = ""; 12 | } 13 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | 3 | Weather 4 | 5 |

Weather

6 | 7 |

This component demonstrates showing data.

8 | 9 | @if (forecasts == null) 10 | { 11 |

Loading...

12 | } 13 | else 14 | { 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @foreach (var forecast in forecasts) 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
36 | } 37 | 38 | @code { 39 | private WeatherForecast[]? forecasts; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | // Simulate asynchronous loading to demonstrate a loading indicator 44 | await Task.Delay(500); 45 | 46 | var startDate = DateOnly.FromDateTime(DateTime.Now); 47 | var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; 48 | forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast 49 | { 50 | Date = startDate.AddDays(index), 51 | TemperatureC = Random.Shared.Next(-20, 55), 52 | Summary = summaries[Random.Shared.Next(summaries.Length)] 53 | }).ToArray(); 54 | } 55 | 56 | private class WeatherForecast 57 | { 58 | public DateOnly Date { get; set; } 59 | public int TemperatureC { get; set; } 60 | public string? Summary { get; set; } 61 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.SignalR.Client; 2 | global using BlazorDB; 3 | global using System.Net.Http.Json; 4 | global using Newtonsoft.Json; 5 | global using System.Net; 6 | global using System.Linq.Expressions; 7 | global using AvnRepository; 8 | global using RepositoryDemo.Client.Models; 9 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 10 | using RepositoryDemo.Client.Services; 11 | using RepositoryDemo.Client; 12 | 13 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 14 | 15 | builder.Services.AddScoped(sp => new HttpClient 16 | { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 17 | 18 | builder.Services.AddScoped(); 19 | 20 | builder.Services.AddBlazorDB(options => 21 | { 22 | options.Name = "RepositoryDemo"; 23 | options.Version = 1; 24 | 25 | // List all your entities here, but as StoreSchema objects 26 | options.StoreSchemas = new List() 27 | { 28 | new StoreSchema() 29 | { 30 | Name = "Customer", // Name of entity 31 | PrimaryKey = "Id", // Primary Key of entity 32 | PrimaryKeyAuto = true, // Whether or not the Primary key is generated 33 | Indexes = new List { "Id" } 34 | }, 35 | new StoreSchema() 36 | { 37 | Name = $"Customer{Globals.LocalTransactionsSuffix}", 38 | PrimaryKey = "Id", 39 | PrimaryKeyAuto = true, 40 | Indexes = new List { "Id" } 41 | }, 42 | new StoreSchema() 43 | { 44 | Name = $"Customer{Globals.KeysSuffix}", 45 | PrimaryKey = "Id", 46 | PrimaryKeyAuto = true, 47 | Indexes = new List { "Id" } 48 | } 49 | }; 50 | }); 51 | 52 | builder.Services.AddScoped(); 53 | builder.Services.AddScoped(); 54 | 55 | await builder.Build().RunAsync(); 56 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/RepositoryDemo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/CustomerIndexedDBRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerIndexedDBRepository : IndexedDBRepository 4 | { 5 | public CustomerIndexedDBRepository(IBlazorDbFactory dbFactory) 6 | : base("RepositoryDemo", "Id", true, dbFactory) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/CustomerIndexedDBSyncRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | namespace RepositoryDemo.Client.Services; 3 | public class CustomerIndexedDBSyncRepository : IndexedDBSyncRepository 4 | { 5 | public CustomerIndexedDBSyncRepository(IBlazorDbFactory dbFactory, 6 | CustomerRepository customerRepository, 7 | IJSRuntime jsRuntime, 8 | HttpClient httpClient) 9 | : base("RepositoryDemo", 10 | "Id", 11 | true, 12 | dbFactory, 13 | customerRepository, 14 | jsRuntime, 15 | httpClient) 16 | { } 17 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class CustomerRepository : APIRepository 4 | { 5 | HttpClient http; 6 | 7 | // swap out the controller name 8 | //static string controllerName = "inmemorycustomers"; 9 | //static string controllerName = "efcustomers"; 10 | static string controllerName = "dappercustomers"; 11 | 12 | public CustomerRepository(HttpClient _http) 13 | : base(_http, controllerName, "Id") 14 | { 15 | http = _http; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/DataChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class DataChangedEventArgs : EventArgs 4 | { 5 | public string Table { get; set; } 6 | public string Action { get; set; } 7 | public string Id { get; set; } 8 | 9 | public DataChangedEventArgs(string table, string action, string id) 10 | { 11 | Table = table; 12 | Action = action; 13 | Id = id; 14 | } 15 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/LocalTransaction.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class LocalTransaction 4 | { 5 | public TEntity Entity { get; set; } 6 | public LocalTransactionTypes Action { get; set; } 7 | public string ActionName { get; set; } 8 | public object Id { get; set; } 9 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/LocalTransactionTypes.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public enum LocalTransactionTypes 4 | { 5 | Insert = 0, 6 | Update = 1, 7 | Delete = 2, 8 | DeleteAll = 3 9 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/OnlineOfflineKey.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class OnlineOfflineKey 4 | { 5 | public int Id { get; set; } 6 | public object OnlineId { get; set; } 7 | public object LocalId { get; set; } 8 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/Services/OnlineStatusEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Client.Services; 2 | 3 | public class OnlineStatusEventArgs : EventArgs 4 | { 5 | public bool IsOnline { get; set; } 6 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo.Client 10 | @using RepositoryDemo.Client.Models 11 | @using RepositoryDemo.Client.Services 12 | @using AvnRepository 13 | @using BlazorDB -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-off.png -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/RepositoryDemo/RepositoryDemo.Client/wwwroot/images/internet-on.png -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Components/_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 static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using RepositoryDemo 10 | @using RepositoryDemo.Client 11 | @using RepositoryDemo.Components 12 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Data/DapperSqlHelper.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class DapperSqlHelper 4 | { 5 | public static string GetDapperInsertStatement(object Entity, string TableName) 6 | { 7 | // let's get the SQL string started. 8 | string sql = $"insert into {TableName} ("; 9 | 10 | // Get the type, and the list of public properties 11 | var EntityType = Entity.GetType(); 12 | var Properties = EntityType.GetProperties(); 13 | 14 | foreach (var property in Properties) 15 | { 16 | // Is this property nullable? 17 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 18 | { 19 | // yes. get the value. 20 | var value = property.GetValue(Entity); 21 | // is the value null? 22 | if (value != null) 23 | // only add if the value is not null 24 | sql += $"{property.Name}, "; 25 | } 26 | // is this property virtual (like Customer.Invoices)? 27 | else if (property.GetGetMethod().IsVirtual == false) 28 | { 29 | // not virtual. Include 30 | sql += $"{property.Name}, "; 31 | } 32 | } 33 | 34 | // At this point there is a trailing ", " that we need to remove 35 | sql = sql.Substring(0, sql.Length - 2); 36 | 37 | // add the start of the values clause 38 | sql += ") values ("; 39 | 40 | // Once more through the properties 41 | foreach (var property in Properties) 42 | { 43 | if (Nullable.GetUnderlyingType(property.PropertyType) != null) 44 | { 45 | var value = property.GetValue(Entity); 46 | if (value != null) 47 | // inserts in Dapper are paramterized, so at least 48 | // we don't have to figure out data types, quotes, etc. 49 | sql += $"@{property.Name}, "; 50 | } 51 | else if (property.GetGetMethod().IsVirtual == false) 52 | { 53 | sql += $"@{property.Name}, "; 54 | } 55 | } 56 | 57 | // again, remove the trailing ", " and finish with a closed paren 58 | sql = sql.Substring(0, sql.Length - 2) + ")"; 59 | 60 | // we're outta here! 61 | return sql; 62 | } 63 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Data/EFRepository.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public class EFRepository : IRepository 4 | where TEntity : class 5 | where TDataContext : DbContext 6 | { 7 | protected readonly TDataContext context; 8 | internal DbSet dbSet; 9 | 10 | public EFRepository(TDataContext dataContext) 11 | { 12 | context = dataContext; 13 | context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 14 | dbSet = context.Set(); 15 | } 16 | public async Task> GetAllAsync() 17 | { 18 | return await Task.FromResult(dbSet); 19 | } 20 | 21 | public async Task GetByIdAsync(object Id) 22 | { 23 | return await dbSet.FindAsync(Id); 24 | } 25 | 26 | public async Task> GetAsync(QueryFilter Filter) 27 | { 28 | var allitems = (await GetAllAsync()).ToList(); 29 | return Filter.GetFilteredList(allitems); 30 | } 31 | 32 | public async Task InsertAsync(TEntity entity) 33 | { 34 | await dbSet.AddAsync(entity); 35 | await context.SaveChangesAsync(); 36 | return entity; 37 | } 38 | 39 | public async Task UpdateAsync(TEntity entityToUpdate) 40 | { 41 | var dbSet = context.Set(); 42 | dbSet.Attach(entityToUpdate); 43 | context.Entry(entityToUpdate).State = EntityState.Modified; 44 | await context.SaveChangesAsync(); 45 | return entityToUpdate; 46 | } 47 | 48 | public async Task DeleteAsync(TEntity entityToDelete) 49 | { 50 | if (context.Entry(entityToDelete).State == EntityState.Detached) 51 | { 52 | dbSet.Attach(entityToDelete); 53 | } 54 | dbSet.Remove(entityToDelete); 55 | return await context.SaveChangesAsync() >= 1; 56 | } 57 | 58 | public async Task DeleteByIdAsync(object id) 59 | { 60 | TEntity entityToDelete = await dbSet.FindAsync(id); 61 | return await DeleteAsync(entityToDelete); 62 | } 63 | 64 | public async Task DeleteAllAsync() 65 | { 66 | await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE Customer"); 67 | } 68 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Data/RepositoryDemoContext.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo.Data; 2 | 3 | public partial class RepositoryDemoContext : DbContext 4 | { 5 | public RepositoryDemoContext() 6 | { 7 | } 8 | 9 | public RepositoryDemoContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public virtual DbSet Customers { get; set; } 15 | 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 17 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. 18 | => optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=RepositoryDemo;Trusted_Connection=True;"); 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.HasKey(e => e.Id).HasName("PK__Customer__3214EC07A92CECC5"); 25 | 26 | entity.ToTable("Customer"); 27 | 28 | entity.Property(e => e.Email).HasMaxLength(50); 29 | entity.Property(e => e.Name).HasMaxLength(50); 30 | }); 31 | 32 | OnModelCreatingPartial(modelBuilder); 33 | } 34 | 35 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 36 | } 37 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/DataSyncHub.cs: -------------------------------------------------------------------------------- 1 | namespace RepositoryDemo; 2 | 3 | public class DataSyncHub : Hub 4 | { 5 | public async Task SyncRecord(string Table, string Action, string Id) 6 | { 7 | await Clients.Others.SendAsync("ReceiveSyncRecord", 8 | Table, Action, Id); 9 | } 10 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.SignalR; 2 | global using System.Reflection; 3 | global using AvnRepository; 4 | global using Microsoft.EntityFrameworkCore; 5 | global using RepositoryDemo.Client.Models; 6 | global using System.Data.SqlClient; 7 | global using Dapper; 8 | global using Dapper.Contrib.Extensions; 9 | global using System.Data; 10 | using RepositoryDemo.Components; 11 | using RepositoryDemo.Data; 12 | using Microsoft.AspNetCore.ResponseCompression; 13 | using RepositoryDemo; 14 | 15 | var builder = WebApplication.CreateBuilder(args); 16 | 17 | // Add services to the container. 18 | builder.Services.AddRazorComponents() 19 | .AddInteractiveWebAssemblyComponents(); 20 | 21 | builder.Services.AddSingleton>(x => 22 | new MemoryRepository("Id")); 23 | 24 | builder.Services.AddTransient(); 25 | builder.Services.AddTransient>(); 26 | 27 | builder.Services.AddTransient>(s => 28 | new DapperRepository( 29 | builder.Configuration.GetConnectionString("RepositoryDemoConnectionString"))); 30 | 31 | builder.Services.AddControllers(); 32 | 33 | builder.Services.AddSignalR(); 34 | builder.Services.AddResponseCompression(opts => 35 | { 36 | opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat( 37 | new[] { "application/octet-stream" }); 38 | }); 39 | 40 | var app = builder.Build(); 41 | 42 | app.MapControllers(); 43 | 44 | // Configure the HTTP request pipeline. 45 | if (app.Environment.IsDevelopment()) 46 | { 47 | app.UseWebAssemblyDebugging(); 48 | } 49 | else 50 | { 51 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 52 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 53 | app.UseHsts(); 54 | } 55 | 56 | app.UseHttpsRedirection(); 57 | 58 | app.UseStaticFiles(); 59 | app.UseAntiforgery(); 60 | app.UseResponseCompression(); 61 | 62 | app.MapRazorComponents() 63 | .AddInteractiveWebAssemblyRenderMode() 64 | .AddAdditionalAssemblies(typeof(RepositoryDemo.Client._Imports).Assembly); 65 | 66 | app.MapHub("/DataSyncHub"); 67 | 68 | app.Run(); 69 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:21403", 8 | "sslPort": 44307 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5100", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7148;http://localhost:5101", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/RepositoryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "RepositoryDemoConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RepositoryDemo;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" 11 | } 12 | } -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/RepositoryDemo/RepositoryDemo/wwwroot/favicon.png -------------------------------------------------------------------------------- /RepositoryDemo/RepositoryDemo/wwwroot/js/connectivity.js: -------------------------------------------------------------------------------- 1 | let notify; 2 | 3 | window.connectivity = { 4 | initialize: function (interop) { 5 | 6 | notify = function () { 7 | interop.invokeMethodAsync("ConnectivityChanged", navigator.onLine); 8 | } 9 | 10 | window.addEventListener("online", notify); 11 | window.addEventListener("offline", notify); 12 | 13 | notify(navigator.onLine); 14 | }, 15 | dispose: function () { 16 | 17 | if (notify != null) { 18 | 19 | window.removeEventListener("online", notify); 20 | window.removeEventListener("offline", notify); 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /images/image-20210514130755412.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514130755412.png -------------------------------------------------------------------------------- /images/image-20210514130900651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514130900651.png -------------------------------------------------------------------------------- /images/image-20210514130923188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514130923188.png -------------------------------------------------------------------------------- /images/image-20210514131058298.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514131058298.png -------------------------------------------------------------------------------- /images/image-20210514131219347.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514131219347.png -------------------------------------------------------------------------------- /images/image-20210514131241975.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514131241975.png -------------------------------------------------------------------------------- /images/image-20210514131305800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514131305800.png -------------------------------------------------------------------------------- /images/image-20210514131416003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20210514131416003.png -------------------------------------------------------------------------------- /images/image-20220322175149382.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220322175149382.png -------------------------------------------------------------------------------- /images/image-20220323174510706.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220323174510706.png -------------------------------------------------------------------------------- /images/image-20220323174917490.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220323174917490.png -------------------------------------------------------------------------------- /images/image-20220323175142470.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220323175142470.png -------------------------------------------------------------------------------- /images/image-20220323191200976.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220323191200976.png -------------------------------------------------------------------------------- /images/image-20220410180617860.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220410180617860.png -------------------------------------------------------------------------------- /images/image-20220609211836651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220609211836651.png -------------------------------------------------------------------------------- /images/image-20220609212248231.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220609212248231.png -------------------------------------------------------------------------------- /images/image-20220609212350230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20220609212350230.png -------------------------------------------------------------------------------- /images/image-20240423085451476.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423085451476.png -------------------------------------------------------------------------------- /images/image-20240423093155261.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423093155261.png -------------------------------------------------------------------------------- /images/image-20240423094028562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423094028562.png -------------------------------------------------------------------------------- /images/image-20240423094115654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423094115654.png -------------------------------------------------------------------------------- /images/image-20240423095032139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423095032139.png -------------------------------------------------------------------------------- /images/image-20240423114717798.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423114717798.png -------------------------------------------------------------------------------- /images/image-20240423115656651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423115656651.png -------------------------------------------------------------------------------- /images/image-20240423120105891.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423120105891.png -------------------------------------------------------------------------------- /images/image-20240423120221785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlfranklin/BlazorRepositoryDemo/917b73a3db971a13f62375047e1d4569f318f672/images/image-20240423120221785.png --------------------------------------------------------------------------------