Merge upgrade-to-NET8: .NET Framework 4.8.1 → .NET 10.0 + appsettings.json migration
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,220 @@
|
||||
# Projects and dependencies analysis
|
||||
|
||||
This document provides a comprehensive overview of the projects and their dependencies in the context of upgrading to .NETCoreApp,Version=v10.0.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Executive Summary](#executive-Summary)
|
||||
- [Highlevel Metrics](#highlevel-metrics)
|
||||
- [Projects Compatibility](#projects-compatibility)
|
||||
- [Package Compatibility](#package-compatibility)
|
||||
- [API Compatibility](#api-compatibility)
|
||||
- [Aggregate NuGet packages details](#aggregate-nuget-packages-details)
|
||||
- [Top API Migration Challenges](#top-api-migration-challenges)
|
||||
- [Technologies and Features](#technologies-and-features)
|
||||
- [Most Frequent API Issues](#most-frequent-api-issues)
|
||||
- [Projects Relationship Graph](#projects-relationship-graph)
|
||||
- [Project Details](#project-details)
|
||||
|
||||
- [HorseRacingPredictor\BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj)
|
||||
|
||||
|
||||
## Executive Summary
|
||||
|
||||
### Highlevel Metrics
|
||||
|
||||
| Metric | Count | Status |
|
||||
| :--- | :---: | :--- |
|
||||
| Total Projects | 1 | All require upgrade |
|
||||
| Total NuGet Packages | 26 | 9 need upgrade |
|
||||
| Total Code Files | 39 | |
|
||||
| Total Code Files with Incidents | 27 | |
|
||||
| Total Lines of Code | 7140 | |
|
||||
| Total Number of Issues | 2202 | |
|
||||
| Estimated LOC to modify | 2185+ | at least 30,6% of codebase |
|
||||
|
||||
### Projects Compatibility
|
||||
|
||||
| Project | Target Framework | Difficulty | Package Issues | API Issues | Est. LOC Impact | Description |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: | :--- |
|
||||
| [HorseRacingPredictor\BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | net481 | 🟡 Medium | 15 | 2185 | 2185+ | ClassicWpf, Sdk Style = False |
|
||||
|
||||
### Package Compatibility
|
||||
|
||||
| Status | Count | Percentage |
|
||||
| :--- | :---: | :---: |
|
||||
| ✅ Compatible | 17 | 65,4% |
|
||||
| ⚠️ Incompatible | 0 | 0,0% |
|
||||
| 🔄 Upgrade Recommended | 9 | 34,6% |
|
||||
| ***Total NuGet Packages*** | ***26*** | ***100%*** |
|
||||
|
||||
### API Compatibility
|
||||
|
||||
| Category | Count | Impact |
|
||||
| :--- | :---: | :--- |
|
||||
| 🔴 Binary Incompatible | 897 | High - Require code changes |
|
||||
| 🟡 Source Incompatible | 1264 | Medium - Needs re-compilation and potential conflicting API error fixing |
|
||||
| 🔵 Behavioral change | 24 | Low - Behavioral changes that may require testing at runtime |
|
||||
| ✅ Compatible | 10445 | |
|
||||
| ***Total APIs Analyzed*** | ***12630*** | |
|
||||
|
||||
## Aggregate NuGet packages details
|
||||
|
||||
| Package | Current Version | Suggested Version | Projects | Description |
|
||||
| :--- | :---: | :---: | :--- | :--- |
|
||||
| CsvHelper | 33.1.0 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.Bcl.AsyncInterfaces | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| Microsoft.Bcl.HashCode | 6.0.0 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.Bcl.Numerics | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| Microsoft.CSharp | 4.7.0 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.ML | 5.0.0-preview.25503.2 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.ML.CpuMath | 5.0.0-preview.25503.2 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.ML.DataView | 5.0.0-preview.25503.2 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.ML.FastTree | 5.0.0-preview.25503.2 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Microsoft.Web.WebView2 | 1.0.3800.47 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| Newtonsoft.Json | 13.0.4 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| RestSharp | 112.1.1-alpha.0.4 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| System.Buffers | 4.6.1 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
| System.CodeDom | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Collections.Immutable | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.IO.Pipelines | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Memory | 4.6.3 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
| System.Numerics.Tensors | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Numerics.Vectors | 4.6.1 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
| System.Reflection.Emit.Lightweight | 4.7.0 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
| System.Runtime.CompilerServices.Unsafe | 6.1.2 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | ✅Compatible |
|
||||
| System.Text.Encodings.Web | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Text.Json | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Threading.Channels | 10.0.0-rc.1.25451.107 | 10.0.5 | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | È consigliabile eseguire l'aggiornamento del pacchetto NuGet |
|
||||
| System.Threading.Tasks.Extensions | 4.6.3 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
| System.ValueTuple | 4.6.1 | | [BettingPredictor.csproj](#horseracingpredictorbettingpredictorcsproj) | Le funzionalità del pacchetto NuGet sono incluse nel riferimento al framework. |
|
||||
|
||||
## Top API Migration Challenges
|
||||
|
||||
### Technologies and Features
|
||||
|
||||
| Technology | Issues | Percentage | Migration Path |
|
||||
| :--- | :---: | :---: | :--- |
|
||||
| WPF (Windows Presentation Foundation) | 566 | 25,9% | WPF APIs for building Windows desktop applications with XAML-based UI that are available in .NET on Windows. WPF provides rich desktop UI capabilities with data binding and styling. Enable Windows Desktop support: Option 1 (Recommended): Target net9.0-windows; Option 2: Add <UseWindowsDesktop>true</UseWindowsDesktop>. |
|
||||
| Windows Forms | 18 | 0,8% | Windows Forms APIs for building Windows desktop applications with traditional Forms-based UI that are available in .NET on Windows. Enable Windows Desktop support: Option 1 (Recommended): Target net9.0-windows; Option 2: Add <UseWindowsDesktop>true</UseWindowsDesktop>; Option 3 (Legacy): Use Microsoft.NET.Sdk.WindowsDesktop SDK. |
|
||||
| Legacy Configuration System | 2 | 0,1% | Legacy XML-based configuration system (app.config/web.config) that has been replaced by a more flexible configuration model in .NET Core. The old system was rigid and XML-based. Migrate to Microsoft.Extensions.Configuration with JSON/environment variables; use System.Configuration.ConfigurationManager NuGet package as interim bridge if needed. |
|
||||
|
||||
### Most Frequent API Issues
|
||||
|
||||
| API | Count | Percentage | Category |
|
||||
| :--- | :---: | :---: | :--- |
|
||||
| T:System.Data.SqlClient.SqlParameterCollection | 273 | 12,5% | Source Incompatible |
|
||||
| P:System.Data.SqlClient.SqlCommand.Parameters | 273 | 12,5% | Source Incompatible |
|
||||
| T:System.Data.SqlClient.SqlParameter | 273 | 12,5% | Source Incompatible |
|
||||
| M:System.Data.SqlClient.SqlParameterCollection.AddWithValue(System.String,System.Object) | 270 | 12,4% | Source Incompatible |
|
||||
| T:System.Windows.Controls.TextBox | 81 | 3,7% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.TextBlock | 57 | 2,6% | Binary Incompatible |
|
||||
| T:System.Windows.RoutedEventHandler | 52 | 2,4% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.TextBox.Text | 47 | 2,2% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.Button | 42 | 1,9% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.ComboBox | 41 | 1,9% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.TextBlock.Text | 40 | 1,8% | Binary Incompatible |
|
||||
| T:System.Windows.Visibility | 40 | 1,8% | Binary Incompatible |
|
||||
| T:System.Data.SqlClient.SqlConnection | 35 | 1,6% | Source Incompatible |
|
||||
| T:System.Data.SqlClient.SqlCommand | 33 | 1,5% | Source Incompatible |
|
||||
| T:System.Windows.MessageBoxImage | 30 | 1,4% | Binary Incompatible |
|
||||
| T:System.Windows.MessageBoxButton | 30 | 1,4% | Binary Incompatible |
|
||||
| M:System.Data.SqlClient.SqlCommand.ExecuteNonQuery | 23 | 1,1% | Source Incompatible |
|
||||
| M:System.Data.SqlClient.SqlCommand.#ctor(System.String,System.Data.SqlClient.SqlConnection) | 22 | 1,0% | Source Incompatible |
|
||||
| T:System.Windows.Controls.DatePicker | 22 | 1,0% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.CheckBox | 20 | 0,9% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.RadioButton | 19 | 0,9% | Binary Incompatible |
|
||||
| T:System.Windows.RoutedEventArgs | 17 | 0,8% | Binary Incompatible |
|
||||
| T:System.Text.Json.JsonDocument | 16 | 0,7% | Behavioral Change |
|
||||
| T:System.Windows.Controls.SelectionChangedEventHandler | 16 | 0,7% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.TextChangedEventHandler | 16 | 0,7% | Binary Incompatible |
|
||||
| F:System.Windows.MessageBoxButton.OK | 15 | 0,7% | Binary Incompatible |
|
||||
| T:System.Windows.MessageBox | 15 | 0,7% | Binary Incompatible |
|
||||
| T:System.Windows.MessageBoxResult | 15 | 0,7% | Binary Incompatible |
|
||||
| M:System.Windows.MessageBox.Show(System.String,System.String,System.Windows.MessageBoxButton,System.Windows.MessageBoxImage) | 15 | 0,7% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.ProgressBar | 14 | 0,6% | Binary Incompatible |
|
||||
| P:System.Windows.UIElement.IsEnabled | 14 | 0,6% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.ContentControl.Content | 13 | 0,6% | Binary Incompatible |
|
||||
| T:System.Windows.Controls.Grid | 12 | 0,5% | Binary Incompatible |
|
||||
| M:System.Data.SqlClient.SqlCommand.#ctor(System.String,System.Data.SqlClient.SqlConnection,System.Data.SqlClient.SqlTransaction) | 11 | 0,5% | Source Incompatible |
|
||||
| E:System.Windows.Controls.Primitives.ButtonBase.Click | 11 | 0,5% | Binary Incompatible |
|
||||
| T:System.Data.SqlClient.SqlTransaction | 10 | 0,5% | Source Incompatible |
|
||||
| E:System.Windows.Controls.Primitives.ToggleButton.Checked | 10 | 0,5% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.DatePicker.SelectedDate | 10 | 0,5% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.Primitives.RangeBase.Value | 10 | 0,5% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.Primitives.Selector.SelectedItem | 9 | 0,4% | Binary Incompatible |
|
||||
| P:System.Windows.Controls.Primitives.ToggleButton.IsChecked | 9 | 0,4% | Binary Incompatible |
|
||||
| M:System.Data.SqlClient.SqlCommand.ExecuteScalar | 8 | 0,4% | Source Incompatible |
|
||||
| T:System.Windows.Controls.DataGrid | 8 | 0,4% | Binary Incompatible |
|
||||
| F:System.Windows.Visibility.Visible | 8 | 0,4% | Binary Incompatible |
|
||||
| F:System.Windows.Visibility.Collapsed | 8 | 0,4% | Binary Incompatible |
|
||||
| P:System.Windows.UIElement.Visibility | 8 | 0,4% | Binary Incompatible |
|
||||
| E:System.Windows.Controls.Primitives.Selector.SelectionChanged | 8 | 0,4% | Binary Incompatible |
|
||||
| E:System.Windows.Controls.Primitives.TextBoxBase.TextChanged | 8 | 0,4% | Binary Incompatible |
|
||||
| F:System.Windows.MessageBoxImage.Error | 7 | 0,3% | Binary Incompatible |
|
||||
| T:System.Uri | 6 | 0,3% | Behavioral Change |
|
||||
|
||||
## Projects Relationship Graph
|
||||
|
||||
Legend:
|
||||
📦 SDK-style project
|
||||
⚙️ Classic project
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P1["<b>⚙️ BettingPredictor.csproj</b><br/><small>net481</small>"]
|
||||
click P1 "#horseracingpredictorbettingpredictorcsproj"
|
||||
|
||||
```
|
||||
|
||||
## Project Details
|
||||
|
||||
<a id="horseracingpredictorbettingpredictorcsproj"></a>
|
||||
### HorseRacingPredictor\BettingPredictor.csproj
|
||||
|
||||
#### Project Info
|
||||
|
||||
- **Current Target Framework:** net481
|
||||
- **Proposed Target Framework:** net10.0-windows
|
||||
- **SDK-style**: False
|
||||
- **Project Kind:** ClassicWpf
|
||||
- **Dependencies**: 0
|
||||
- **Dependants**: 0
|
||||
- **Number of Files**: 43
|
||||
- **Number of Files with Incidents**: 27
|
||||
- **Lines of Code**: 7140
|
||||
- **Estimated LOC to modify**: 2185+ (at least 30,6% of the project)
|
||||
|
||||
#### Dependency Graph
|
||||
|
||||
Legend:
|
||||
📦 SDK-style project
|
||||
⚙️ Classic project
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph current["BettingPredictor.csproj"]
|
||||
MAIN["<b>⚙️ BettingPredictor.csproj</b><br/><small>net481</small>"]
|
||||
click MAIN "#horseracingpredictorbettingpredictorcsproj"
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
### API Compatibility
|
||||
|
||||
| Category | Count | Impact |
|
||||
| :--- | :---: | :--- |
|
||||
| 🔴 Binary Incompatible | 897 | High - Require code changes |
|
||||
| 🟡 Source Incompatible | 1264 | Medium - Needs re-compilation and potential conflicting API error fixing |
|
||||
| 🔵 Behavioral change | 24 | Low - Behavioral changes that may require testing at runtime |
|
||||
| ✅ Compatible | 10445 | |
|
||||
| ***Total APIs Analyzed*** | ***12630*** | |
|
||||
|
||||
#### Project Technologies and Features
|
||||
|
||||
| Technology | Issues | Percentage | Migration Path |
|
||||
| :--- | :---: | :---: | :--- |
|
||||
| Legacy Configuration System | 2 | 0,1% | Legacy XML-based configuration system (app.config/web.config) that has been replaced by a more flexible configuration model in .NET Core. The old system was rigid and XML-based. Migrate to Microsoft.Extensions.Configuration with JSON/environment variables; use System.Configuration.ConfigurationManager NuGet package as interim bridge if needed. |
|
||||
| Windows Forms | 18 | 0,8% | Windows Forms APIs for building Windows desktop applications with traditional Forms-based UI that are available in .NET on Windows. Enable Windows Desktop support: Option 1 (Recommended): Target net9.0-windows; Option 2: Add <UseWindowsDesktop>true</UseWindowsDesktop>; Option 3 (Legacy): Use Microsoft.NET.Sdk.WindowsDesktop SDK. |
|
||||
| WPF (Windows Presentation Foundation) | 566 | 25,9% | WPF APIs for building Windows desktop applications with XAML-based UI that are available in .NET on Windows. WPF provides rich desktop UI capabilities with data binding and styling. Enable Windows Desktop support: Option 1 (Recommended): Target net9.0-windows; Option 2: Add <UseWindowsDesktop>true</UseWindowsDesktop>. |
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
## [2026-03-31 21:08] TASK-001: Verify Prerequisites
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Verified**:
|
||||
- .NET 10.0 SDK is installed and compatible
|
||||
- No global.json found, no conflicts to resolve
|
||||
|
||||
### Outcome
|
||||
Success - All prerequisites verified. Environment ready for .NET 10.0 upgrade.
|
||||
|
||||
|
||||
## [2026-03-31 21:16] TASK-002: Convert Project to SDK-style
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Files Modified**: HorseRacingPredictor/HorseRacingPredictor/BettingPredictor.csproj
|
||||
- **Code Changes**: Converted from classic WPF project to SDK-style format using conversion tool. Removed 8 malformed duplicate PackageReference entries (RestSharp.112.1.1-alpha, System.CodeDom.10.0.0-rc, etc.) that were created by the conversion tool alongside the correct entries.
|
||||
- **Verified**:
|
||||
- Project uses `<Project Sdk="Microsoft.NET.Sdk">` format
|
||||
- `<UseWPF>true</UseWPF>` is present
|
||||
- `<UseWindowsForms>true</UseWindowsForms>` is present
|
||||
- All source files properly included via globbing
|
||||
- Malformed duplicate package entries cleaned up
|
||||
|
||||
### Outcome
|
||||
Success - Project successfully converted to SDK-style format with cleanup.
|
||||
|
||||
|
||||
## [2026-03-31 21:21] TASK-003: Update Target Framework and Project Configuration
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Files Modified**: HorseRacingPredictor/HorseRacingPredictor/BettingPredictor.csproj
|
||||
- **Code Changes**: Changed TargetFramework from net481 to net10.0-windows. Removed unnecessary ImportWindowsDesktopTargets property.
|
||||
- **Verified**:
|
||||
- TargetFramework is net10.0-windows
|
||||
- UseWPF is true
|
||||
- UseWindowsForms is true
|
||||
- ImportWindowsDesktopTargets removed (redundant with -windows TFM)
|
||||
|
||||
### Outcome
|
||||
Success - Target framework updated to net10.0-windows.
|
||||
|
||||
|
||||
## [2026-03-31 21:30] TASK-004: Update NuGet Package References
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Files Modified**: HorseRacingPredictor/HorseRacingPredictor/BettingPredictor.csproj
|
||||
- **Code Changes**:
|
||||
- Removed legacy assembly References (System.Web, System.Data.DataSetExtensions, Microsoft.CSharp, System.Net.Http)
|
||||
- Updated Microsoft.Bcl.AsyncInterfaces, Microsoft.Bcl.Numerics to 10.0.5
|
||||
- Updated System.Numerics.Tensors to 10.0.5
|
||||
- Removed 13 framework-included packages: Microsoft.CSharp, System.Buffers, System.CodeDom, System.Collections.Immutable, System.IO.Pipelines, System.Memory, System.Numerics.Vectors, System.Reflection.Emit.Lightweight, System.Runtime.CompilerServices.Unsafe, System.Text.Encodings.Web, System.Text.Json, System.Threading.Channels, System.Threading.Tasks.Extensions, System.ValueTuple
|
||||
- Added Microsoft.Data.SqlClient 7.0.0
|
||||
- Added System.Configuration.ConfigurationManager 10.0.5
|
||||
- **Verified**: dotnet restore completed with 0 errors and only 1 informational warning (NU1510)
|
||||
|
||||
### Outcome
|
||||
Success - All NuGet packages updated, removed, or added per plan. Restore clean.
|
||||
|
||||
|
||||
## [2026-03-31 21:38] TASK-005: Migrate SqlClient Namespace
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Files Modified**:
|
||||
- Football/Database/APIResponse.cs, BetType.cs, Bookmaker.cs, Comparison.cs, Fixture.cs, FixtureLeague.cs, Goals.cs, H2H.cs, League.cs, LeagueStats.cs, Odds.cs, Prediction.cs, Score.cs, Team.cs, TeamStats.cs
|
||||
- Football/Manager/Database.cs
|
||||
- Manager/Database.cs
|
||||
- HorseRacing/Main.cs
|
||||
- (Additional file: HorseRacing/Manager/Database.cs if present)
|
||||
- **Code Changes**: Replaced all `System.Data.SqlClient` references with `Microsoft.Data.SqlClient` across 19 files (including using directives and fully-qualified references in Fixture.cs)
|
||||
- **Errors Fixed**: APIResponse.cs was accidentally emptied due to file lock during bulk replace; recovered from git and manually patched
|
||||
- **Verified**:
|
||||
- Zero remaining `System.Data.SqlClient` references
|
||||
- All files confirmed using `Microsoft.Data.SqlClient`
|
||||
|
||||
### Outcome
|
||||
Success - SqlClient namespace migration complete across all 19 affected files.
|
||||
|
||||
|
||||
## [2026-03-31 21:46] TASK-006: Build Solution and Fix Compilation Errors
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Files Deleted**: HorseRacingPredictor/HorseRacingPredictor/CsvHelperStubs.cs
|
||||
- **Code Changes**: Removed CsvHelperStubs.cs which contained placeholder type definitions conflicting with actual CsvHelper 33.1.0 package types (IReaderRow, MemberMapData, DefaultTypeConverter, CsvReader, etc.)
|
||||
- **Errors Fixed**: 4 CS0104 ambiguous reference errors in Manager/FileReader.cs (IReaderRow, MemberMapData) resolved by removing conflicting stub file
|
||||
- **Build Status**: Successful — 0 errors, 1 warning (NU1510 informational)
|
||||
|
||||
### Outcome
|
||||
Success - Solution builds with 0 errors on .NET 10.0.
|
||||
|
||||
|
||||
## [2026-03-31 21:54] TASK-007: Final Verification and Commit
|
||||
|
||||
Status: Complete
|
||||
|
||||
- **Verified**:
|
||||
- Project targets net10.0-windows ✅
|
||||
- Project uses SDK-style format (Microsoft.NET.Sdk) ✅
|
||||
- UseWPF enabled ✅
|
||||
- Microsoft.Data.SqlClient 7.0.0 present ✅
|
||||
- System.Configuration.ConfigurationManager 10.0.5 present ✅
|
||||
- Zero remaining System.Data.SqlClient references ✅
|
||||
- Framework-included packages removed ✅
|
||||
- Solution builds with 0 errors ✅
|
||||
- **Commits**: cfb29cc: "Upgrade BettingPredictor from .NET Framework 4.8.1 to .NET 10.0"
|
||||
- **Files Modified**: 24 files (211 insertions, 424 deletions)
|
||||
|
||||
### Outcome
|
||||
Success - All success criteria verified, changes committed on upgrade-to-NET8 branch.
|
||||
|
||||
@@ -0,0 +1,592 @@
|
||||
# .NET 10.0 Upgrade Plan — BettingPredictor
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [1. Executive Summary](#1-executive-summary)
|
||||
- [1.1 Scope & Target](#11-scope--target)
|
||||
- [1.2 Selected Strategy](#12-selected-strategy)
|
||||
- [1.3 Complexity Classification](#13-complexity-classification)
|
||||
- [1.4 Critical Issues](#14-critical-issues)
|
||||
- [2. Migration Strategy](#2-migration-strategy)
|
||||
- [2.1 Approach & Justification](#21-approach--justification)
|
||||
- [2.2 Prerequisites](#22-prerequisites)
|
||||
- [2.3 Implementation Timeline](#23-implementation-timeline)
|
||||
- [3. Detailed Dependency Analysis](#3-detailed-dependency-analysis)
|
||||
- [4. Project-by-Project Plans](#4-project-by-project-plans)
|
||||
- [4.1 BettingPredictor.csproj](#41-bettingpredictorcsproj)
|
||||
- [5. Package Update Reference](#5-package-update-reference)
|
||||
- [5.1 Packages to Update](#51-packages-to-update)
|
||||
- [5.2 Packages to Remove (Framework-Included)](#52-packages-to-remove-framework-included)
|
||||
- [5.3 Packages to Add](#53-packages-to-add)
|
||||
- [5.4 Compatible Packages (No Change)](#54-compatible-packages-no-change)
|
||||
- [6. Breaking Changes Catalog](#6-breaking-changes-catalog)
|
||||
- [6.1 SqlClient Namespace Migration](#61-sqlclient-namespace-migration)
|
||||
- [6.2 WPF Binary Incompatibilities](#62-wpf-binary-incompatibilities)
|
||||
- [6.3 Windows Forms API References](#63-windows-forms-api-references)
|
||||
- [6.4 Legacy Configuration System](#64-legacy-configuration-system)
|
||||
- [6.5 Behavioral Changes](#65-behavioral-changes)
|
||||
- [7. Testing & Validation Strategy](#7-testing--validation-strategy)
|
||||
- [8. Risk Management](#8-risk-management)
|
||||
- [9. Complexity & Effort Assessment](#9-complexity--effort-assessment)
|
||||
- [10. Source Control Strategy](#10-source-control-strategy)
|
||||
- [11. Success Criteria](#11-success-criteria)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
### 1.1 Scope & Target
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Solution** | BettingPredictor.sln |
|
||||
| **Projects** | 1 (BettingPredictor.csproj) |
|
||||
| **Project Type** | Classic WPF (non SDK-style) |
|
||||
| **Current Framework** | .NET Framework 4.8.1 (net481) |
|
||||
| **Target Framework** | .NET 10.0 (net10.0-windows) |
|
||||
| **Total LOC** | 7,140 |
|
||||
| **Total Files** | 39 (27 with compatibility issues) |
|
||||
| **NuGet Packages** | 26 total — 9 to update, 6 to remove (framework-included) |
|
||||
| **Total Issues** | 2,202 |
|
||||
| **Estimated LOC Impact** | 2,185+ (~30.6% of codebase) |
|
||||
|
||||
### 1.2 Selected Strategy
|
||||
|
||||
**All-At-Once Strategy** — Single project upgraded in one atomic operation.
|
||||
|
||||
**Rationale**:
|
||||
- 1 project (well under 30-project threshold)
|
||||
- No inter-project dependencies
|
||||
- Homogeneous codebase (single WPF desktop application)
|
||||
- All 9 packages requiring update have known target versions (stable 10.0.5)
|
||||
- No incompatible packages — all have clear upgrade or removal paths
|
||||
- Fastest completion with single coordinated upgrade
|
||||
|
||||
### 1.3 Complexity Classification
|
||||
|
||||
**Simple** — ?5 projects, dependency depth 0, no security vulnerabilities, no circular dependencies.
|
||||
|
||||
| Criterion | Value | Threshold |
|
||||
|---|---|---|
|
||||
| Project count | 1 | ? 5 ? |
|
||||
| Dependency depth | 0 | ? 2 ? |
|
||||
| Security vulnerabilities | 0 | None ? |
|
||||
| High-risk items | 0 | None ? |
|
||||
|
||||
**Iteration strategy**: Simple batch — all project details in 1–2 detail iterations.
|
||||
|
||||
### 1.4 Critical Issues
|
||||
|
||||
| Priority | Issue | Impact | Resolution |
|
||||
|---|---|---|---|
|
||||
| ?? Mandatory | SDK-style conversion | Project won't build in .NET 10 without SDK-style format | Use SDK-style conversion tool |
|
||||
| ?? Mandatory | Target framework change | net481 ? net10.0-windows | Update TargetFramework in .csproj |
|
||||
| ?? High | SqlClient namespace migration | 1,264 source-incompatible API references across 16+ files | Replace `System.Data.SqlClient` with `Microsoft.Data.SqlClient` |
|
||||
| ?? High | 9 pre-release packages ? stable | rc.1 packages must move to stable 10.0.5 | Update PackageReference versions |
|
||||
| ?? Medium | 6 framework-included packages | Redundant packages may cause conflicts | Remove PackageReference entries |
|
||||
| ?? Low | WPF binary incompatibilities (897) | Resolved automatically by recompilation against .NET 10.0 WPF | No code changes — recompile with net10.0-windows |
|
||||
| ?? Low | Behavioral changes (24) | JsonDocument, System.Uri — may affect runtime behavior | Requires runtime testing and validation |
|
||||
| ?? Low | Legacy configuration (2) | Settings.Designer.cs uses old config system | Add `System.Configuration.ConfigurationManager` NuGet as bridge |
|
||||
|
||||
---
|
||||
|
||||
## 2. Migration Strategy
|
||||
|
||||
### 2.1 Approach & Justification
|
||||
|
||||
**All-At-Once** — All updates performed as a single coordinated atomic operation with no intermediate states.
|
||||
|
||||
This is ideal because:
|
||||
- **Single project**: no dependency ordering needed
|
||||
- **Clear package paths**: all 9 packages have exact target versions (10.0.5 stable)
|
||||
- **No incompatible packages**: 0% incompatible, no blocking issues
|
||||
- **WPF continuity**: WPF is fully supported in .NET 10.0 with `-windows` TFM — same APIs, new runtime
|
||||
- **Risk is contained**: single project means build failures are immediately visible and fixable
|
||||
|
||||
### 2.2 Prerequisites
|
||||
|
||||
Before starting the atomic upgrade:
|
||||
|
||||
1. **Verify .NET 10.0 SDK installation**
|
||||
- Required SDK version: .NET 10.0 or later
|
||||
- Download from: https://dotnet.microsoft.com/download/dotnet/10.0
|
||||
- Verify with: `dotnet --list-sdks`
|
||||
|
||||
2. **Check global.json** (if present)
|
||||
- Ensure it allows .NET 10.0 SDK or update/remove it
|
||||
- If present, update `sdk.version` to a .NET 10.0 compatible version
|
||||
|
||||
3. **Source control**
|
||||
- Working branch: `upgrade-to-NET8` (from `main`)
|
||||
- No pending changes
|
||||
|
||||
### 2.3 Implementation Timeline
|
||||
|
||||
#### Phase 0: Preparation
|
||||
- Verify .NET 10.0 SDK installation
|
||||
- Validate global.json compatibility
|
||||
|
||||
#### Phase 1: Atomic Upgrade
|
||||
**Operations** (performed as single coordinated batch):
|
||||
1. Convert project to SDK-style format
|
||||
2. Update TargetFramework to `net10.0-windows`
|
||||
3. Update all 9 package references to stable versions
|
||||
4. Remove 6 framework-included package references
|
||||
5. Add `Microsoft.Data.SqlClient` package
|
||||
6. Replace all `System.Data.SqlClient` usages with `Microsoft.Data.SqlClient`
|
||||
7. Address legacy configuration bridge (add `System.Configuration.ConfigurationManager` if needed)
|
||||
8. Build solution and fix all compilation errors
|
||||
9. Verify: solution builds with 0 errors
|
||||
|
||||
**Deliverable**: Solution builds successfully targeting net10.0-windows
|
||||
|
||||
#### Phase 2: Validation
|
||||
**Operations**:
|
||||
- No automated test projects exist in the solution
|
||||
- Manual verification of application startup and core functionality
|
||||
- Review behavioral changes (JsonDocument, System.Uri) at runtime
|
||||
|
||||
**Deliverable**: Application runs correctly on .NET 10.0
|
||||
|
||||
---
|
||||
|
||||
## 3. Detailed Dependency Analysis
|
||||
|
||||
This solution contains a **single project** with **zero project dependencies** and **zero dependants**. There is no dependency graph to navigate — the upgrade operates on one isolated project.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P1["BettingPredictor.csproj\nnet481 ? net10.0-windows"]
|
||||
```
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| Total projects | 1 |
|
||||
| Dependency depth | 0 |
|
||||
| Circular dependencies | None |
|
||||
| Critical path | BettingPredictor.csproj (only project) |
|
||||
| Migration phases needed | 1 (atomic) |
|
||||
| Test projects | 0 |
|
||||
|
||||
Since there is only one project with no dependencies, the entire upgrade is executed as a single atomic operation. No phased ordering is required.
|
||||
|
||||
---
|
||||
|
||||
## 4. Project-by-Project Plans
|
||||
|
||||
### 4.1 BettingPredictor.csproj
|
||||
|
||||
#### Current State
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Path** | `HorseRacingPredictor\HorseRacingPredictor\BettingPredictor.csproj` |
|
||||
| **Target Framework** | net481 (.NET Framework 4.8.1) |
|
||||
| **SDK-style** | ? No (Classic WPF format) |
|
||||
| **Project Kind** | ClassicWpf |
|
||||
| **Lines of Code** | 7,140 |
|
||||
| **Files** | 43 total, 27 with incidents |
|
||||
| **NuGet Packages** | 26 (9 update, 6 remove, 11 compatible) |
|
||||
| **Dependencies** | 0 project references |
|
||||
| **Dependants** | 0 |
|
||||
| **Risk Level** | ?? Medium |
|
||||
|
||||
**Key technologies used**:
|
||||
- WPF (Windows Presentation Foundation) — primary UI framework
|
||||
- WebView2 — embedded browser control
|
||||
- Microsoft.ML / ML.NET — machine learning predictions
|
||||
- System.Data.SqlClient — SQL database access
|
||||
- RestSharp — REST API client
|
||||
- CsvHelper — CSV file handling
|
||||
- Newtonsoft.Json & System.Text.Json — JSON serialization
|
||||
|
||||
#### Target State
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Target Framework** | net10.0-windows |
|
||||
| **SDK-style** | ? Yes |
|
||||
| **Updated packages** | 9 |
|
||||
| **Removed packages** | 6 (framework-included) |
|
||||
| **New packages** | Microsoft.Data.SqlClient, System.Configuration.ConfigurationManager |
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
**Step 1: Convert to SDK-style project**
|
||||
- Use the SDK-style conversion tool on `BettingPredictor.csproj`
|
||||
- The conversion tool handles:
|
||||
- Migrating `<PackageReference>` entries from `packages.config`
|
||||
- Setting up the new SDK-style project structure
|
||||
- Preserving existing project configuration
|
||||
- After conversion, the `.csproj` will use `Microsoft.NET.Sdk` format
|
||||
|
||||
**Step 2: Update TargetFramework**
|
||||
- Change `<TargetFramework>` to `net10.0-windows`
|
||||
- This enables WPF and WinForms support via the `-windows` platform specifier
|
||||
- No need for `<UseWPF>true</UseWPF>` separately — SDK-style WPF projects include this automatically when converted
|
||||
|
||||
**Step 3: Update package references (9 packages)**
|
||||
- Update all pre-release (rc.1) packages to stable 10.0.5 versions
|
||||
- See [§5.1 Packages to Update](#51-packages-to-update) for complete version matrix
|
||||
|
||||
**Step 4: Remove framework-included packages (6 packages)**
|
||||
- Remove references to packages whose functionality is now built into .NET 10.0
|
||||
- See [§5.2 Packages to Remove](#52-packages-to-remove-framework-included) for complete list
|
||||
|
||||
**Step 5: Migrate SqlClient**
|
||||
- Add `Microsoft.Data.SqlClient` NuGet package
|
||||
- In all affected files, replace:
|
||||
- `using System.Data.SqlClient;` ? `using Microsoft.Data.SqlClient;`
|
||||
- The API surface is identical — `SqlConnection`, `SqlCommand`, `SqlParameter`, `SqlTransaction` all exist in `Microsoft.Data.SqlClient` with the same signatures
|
||||
- **Affected files** (16 files, ~1,264 API references):
|
||||
- `Manager\Database.cs` (12 issues) — base database class with `GetConnection()`, transactions
|
||||
- `Football\Manager\Database.cs` (10 issues) — football-specific DB operations
|
||||
- `Football\Database\APIResponse.cs` (82 issues)
|
||||
- `Football\Database\LeagueStats.cs` (76 issues)
|
||||
- `Football\Database\Fixture.cs` (67 issues)
|
||||
- `Football\Database\Comparison.cs` (64 issues)
|
||||
- `Football\Database\League.cs` (57 issues)
|
||||
- `Football\Database\TeamStats.cs` (56 issues)
|
||||
- `Football\Database\Prediction.cs` (52 issues)
|
||||
- `Football\Database\Odds.cs` (43 issues)
|
||||
- `Football\Database\Score.cs` (40 issues)
|
||||
- `Football\Database\FixtureLeague.cs` (39 issues)
|
||||
- `Football\Database\H2H.cs` (20 issues)
|
||||
- `Football\Database\Goals.cs` (16 issues)
|
||||
- `Football\Database\BetType.cs` (12 issues)
|
||||
- `Football\Database\Bookmaker.cs` (12 issues)
|
||||
|
||||
**Step 6: Address legacy configuration**
|
||||
- `Properties\Settings.Designer.cs` (2 issues) uses the legacy `System.Configuration` APIs
|
||||
- Add NuGet package `System.Configuration.ConfigurationManager` as an interim bridge
|
||||
- This provides the `ConfigurationManager`, `Settings`, and related classes on .NET 10.0
|
||||
- ?? Long-term recommendation: migrate to `Microsoft.Extensions.Configuration` with `appsettings.json`
|
||||
|
||||
**Step 7: Restore, build, and fix compilation errors**
|
||||
- Run `dotnet restore` to resolve all updated package references
|
||||
- Build the entire solution
|
||||
- Fix any compilation errors discovered (expected areas: SqlClient namespace, removed APIs, configuration)
|
||||
- Rebuild and verify: **0 compilation errors**
|
||||
|
||||
#### Validation Checklist
|
||||
|
||||
- [ ] Project converted to SDK-style
|
||||
- [ ] TargetFramework set to net10.0-windows
|
||||
- [ ] All 9 packages updated to stable versions
|
||||
- [ ] 6 framework-included packages removed
|
||||
- [ ] Microsoft.Data.SqlClient added and all usages migrated
|
||||
- [ ] System.Configuration.ConfigurationManager added (if needed)
|
||||
- [ ] Solution builds with 0 errors
|
||||
- [ ] Solution builds with 0 warnings (best effort)
|
||||
- [ ] Application starts and main UI renders correctly
|
||||
|
||||
---
|
||||
|
||||
## 5. Package Update Reference
|
||||
|
||||
### 5.1 Packages to Update
|
||||
|
||||
All 9 packages are currently on pre-release `10.0.0-rc.1.25451.107` and must be updated to stable `10.0.5`.
|
||||
|
||||
| Package | Current Version | Target Version | Reason |
|
||||
|---|---|---|---|
|
||||
| Microsoft.Bcl.AsyncInterfaces | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| Microsoft.Bcl.Numerics | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.CodeDom | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.Collections.Immutable | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.IO.Pipelines | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.Numerics.Tensors | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.Text.Encodings.Web | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.Text.Json | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
| System.Threading.Channels | 10.0.0-rc.1.25451.107 | 10.0.5 | Pre-release ? stable |
|
||||
|
||||
### 5.2 Packages to Remove (Framework-Included)
|
||||
|
||||
These packages provide functionality that is now built into the .NET 10.0 runtime. Their `PackageReference` entries should be removed to avoid version conflicts.
|
||||
|
||||
| Package | Current Version | Reason for Removal |
|
||||
|---|---|---|
|
||||
| System.Buffers | 4.6.1 | Included in .NET 10.0 runtime |
|
||||
| System.Memory | 4.6.3 | Included in .NET 10.0 runtime |
|
||||
| System.Numerics.Vectors | 4.6.1 | Included in .NET 10.0 runtime |
|
||||
| System.Reflection.Emit.Lightweight | 4.7.0 | Included in .NET 10.0 runtime |
|
||||
| System.Threading.Tasks.Extensions | 4.6.3 | Included in .NET 10.0 runtime |
|
||||
| System.ValueTuple | 4.6.1 | Included in .NET 10.0 runtime |
|
||||
|
||||
### 5.3 Packages to Add
|
||||
|
||||
| Package | Version | Reason |
|
||||
|---|---|---|
|
||||
| Microsoft.Data.SqlClient | Latest stable | Replaces `System.Data.SqlClient` for .NET 10.0 |
|
||||
| System.Configuration.ConfigurationManager | Latest stable for net10.0 | Bridge for legacy `Settings.Designer.cs` configuration |
|
||||
|
||||
### 5.4 Compatible Packages (No Change)
|
||||
|
||||
These packages are already compatible with .NET 10.0 and require no version changes.
|
||||
|
||||
| Package | Current Version | Notes |
|
||||
|---|---|---|
|
||||
| CsvHelper | 33.1.0 | ? Compatible |
|
||||
| Microsoft.Bcl.HashCode | 6.0.0 | ? Compatible |
|
||||
| Microsoft.CSharp | 4.7.0 | ? Compatible |
|
||||
| Microsoft.ML | 5.0.0-preview.25503.2 | ? Compatible (pre-release but no stable alternative) |
|
||||
| Microsoft.ML.CpuMath | 5.0.0-preview.25503.2 | ? Compatible |
|
||||
| Microsoft.ML.DataView | 5.0.0-preview.25503.2 | ? Compatible |
|
||||
| Microsoft.ML.FastTree | 5.0.0-preview.25503.2 | ? Compatible |
|
||||
| Microsoft.Web.WebView2 | 1.0.3800.47 | ? Compatible |
|
||||
| Newtonsoft.Json | 13.0.4 | ? Compatible |
|
||||
| RestSharp | 112.1.1-alpha.0.4 | ? Compatible |
|
||||
| System.Runtime.CompilerServices.Unsafe | 6.1.2 | ? Compatible |
|
||||
|
||||
---
|
||||
|
||||
## 6. Breaking Changes Catalog
|
||||
|
||||
### 6.1 SqlClient Namespace Migration
|
||||
|
||||
**Category**: Source Incompatible
|
||||
**Impact**: 1,264 API references across 16+ files
|
||||
**Severity**: High — requires code changes in every database access file
|
||||
|
||||
**Problem**: `System.Data.SqlClient` is not available as a built-in namespace in .NET 10.0. The legacy `System.Data.SqlClient` namespace shipped with .NET Framework is replaced by the standalone `Microsoft.Data.SqlClient` package.
|
||||
|
||||
**Resolution**:
|
||||
1. Add NuGet package `Microsoft.Data.SqlClient`
|
||||
2. In every file that uses `System.Data.SqlClient`, change:
|
||||
```csharp
|
||||
// Before
|
||||
using System.Data.SqlClient;
|
||||
|
||||
// After
|
||||
using Microsoft.Data.SqlClient;
|
||||
```
|
||||
3. The API surface is **API-compatible** — same class names (`SqlConnection`, `SqlCommand`, `SqlParameter`, `SqlTransaction`, etc.) with identical method signatures
|
||||
4. No logic changes required — only the `using` directive changes
|
||||
|
||||
**Affected classes and methods** (most frequent):
|
||||
- `SqlParameterCollection` / `SqlCommand.Parameters` (273 references)
|
||||
- `SqlParameter` (273 references)
|
||||
- `SqlParameterCollection.AddWithValue()` (270 references)
|
||||
- `SqlConnection` (35 references)
|
||||
- `SqlCommand` (33 references)
|
||||
- `SqlCommand.ExecuteNonQuery()` (23 references)
|
||||
- `SqlCommand.ExecuteScalar()` (8 references)
|
||||
- `SqlTransaction` / `BeginTransaction()` / `Commit()` / `Rollback()` (10 references)
|
||||
|
||||
### 6.2 WPF Binary Incompatibilities
|
||||
|
||||
**Category**: Binary Incompatible
|
||||
**Impact**: 897 API references
|
||||
**Severity**: Low — **resolved automatically by recompilation**
|
||||
|
||||
**Problem**: WPF types in .NET 10.0 are binary-incompatible with .NET Framework 4.8.1 assemblies. The types exist at the same namespaces and with the same API surfaces, but are in different assemblies.
|
||||
|
||||
**Resolution**: No code changes needed. Targeting `net10.0-windows` and recompiling resolves all 897 binary incompatibilities. The WPF APIs (`TextBox`, `TextBlock`, `Button`, `ComboBox`, `DataGrid`, `DatePicker`, `CheckBox`, `RadioButton`, `ProgressBar`, `MessageBox`, `Grid`, `Visibility`, etc.) are fully available in .NET 10.0 WPF.
|
||||
|
||||
### 6.3 Windows Forms API References
|
||||
|
||||
**Category**: Binary Incompatible
|
||||
**Impact**: 18 API references
|
||||
**Severity**: Low — **resolved automatically by targeting net10.0-windows**
|
||||
|
||||
**Problem**: The project uses some Windows Forms APIs (likely `FolderBrowserDialog`, `OpenFileDialog`, or similar interop). These are binary incompatible across frameworks.
|
||||
|
||||
**Resolution**: The `-windows` platform specifier in `net10.0-windows` automatically enables both WPF and WinForms support. Recompilation resolves these references.
|
||||
|
||||
### 6.4 Legacy Configuration System
|
||||
|
||||
**Category**: Source Incompatible
|
||||
**Impact**: 2 API references in `Properties\Settings.Designer.cs`
|
||||
**Severity**: Low
|
||||
|
||||
**Problem**: The legacy `System.Configuration` APIs (`ConfigurationManager`, `ApplicationSettingsBase`, etc.) from `app.config` / `web.config` are not built into .NET 10.0.
|
||||
|
||||
**Resolution**:
|
||||
- Add NuGet package `System.Configuration.ConfigurationManager` to provide the legacy APIs as a bridge
|
||||
- `Properties\Settings.Designer.cs` will compile and work as before
|
||||
- ?? **Long-term recommendation**: Migrate to `Microsoft.Extensions.Configuration` with JSON-based configuration (`appsettings.json`), but this is optional and can be deferred
|
||||
|
||||
### 6.5 Behavioral Changes
|
||||
|
||||
**Category**: Behavioral Change
|
||||
**Impact**: 24 API references (16 JsonDocument, 6 System.Uri, 2 other)
|
||||
**Severity**: Low — no compilation errors, but may cause runtime behavior differences
|
||||
|
||||
#### 6.5.1 System.Text.Json.JsonDocument (16 references)
|
||||
|
||||
**Files affected**: `HorseRacing\API\RacingApiClient.cs`, `HorseRacing\Main.cs`
|
||||
|
||||
**Potential changes in .NET 10.0**:
|
||||
- Stricter JSON parsing by default
|
||||
- Changes to how trailing commas, comments, and edge cases are handled
|
||||
- `JsonSerializerOptions` defaults may differ
|
||||
|
||||
**Mitigation**: Test JSON parsing scenarios thoroughly. If issues arise, configure `JsonSerializerOptions` or `JsonDocumentOptions` to match previous behavior (e.g., `AllowTrailingCommas = true`).
|
||||
|
||||
#### 6.5.2 System.Uri (6 references)
|
||||
|
||||
**Files affected**: `HorseRacing\API\RacingApiClient.cs`, `HorseRacing\Main.cs`
|
||||
|
||||
**Potential changes in .NET 10.0**:
|
||||
- More strict URI parsing conforming to RFC 3986
|
||||
- Changes in how relative URIs and encoding edge cases are handled
|
||||
|
||||
**Mitigation**: Test URI-based operations (API endpoint construction, WebView2 navigation). Typically no issues with standard HTTP/HTTPS URIs.
|
||||
|
||||
---
|
||||
|
||||
## 7. Testing & Validation Strategy
|
||||
|
||||
### 7.1 Automated Tests
|
||||
|
||||
?? **No automated test projects exist** in this solution. There are no unit test, integration test, or other test projects to execute.
|
||||
|
||||
### 7.2 Build Verification
|
||||
|
||||
The primary automated validation is build success:
|
||||
|
||||
- [ ] `dotnet restore` completes without errors
|
||||
- [ ] `dotnet build` completes with **0 errors**
|
||||
- [ ] No NuGet package version conflicts
|
||||
- [ ] No unresolved assembly references
|
||||
|
||||
### 7.3 Runtime Validation Areas
|
||||
|
||||
Since there are no automated tests, the following areas should be manually verified after the upgrade:
|
||||
|
||||
| Area | What to Verify | Risk |
|
||||
|---|---|---|
|
||||
| **Application startup** | App starts, main window renders correctly | Low — WPF is fully supported |
|
||||
| **Navigation** | All 4 pages (Football, Racing, Virtual Football, Settings) load | Low |
|
||||
| **WebView2** | Virtual Football page loads WebView2 browser correctly | Low — WebView2 is compatible |
|
||||
| **Database operations** | Football/Racing data download and storage work (SqlClient migration) | Medium — namespace change |
|
||||
| **API calls** | FormFav and Football API calls succeed (RestSharp + JSON parsing) | Medium — behavioral changes |
|
||||
| **CSV import/export** | CsvHelper operations work correctly | Low — compatible package |
|
||||
| **Settings persistence** | Save/load settings works (legacy config bridge) | Low |
|
||||
| **ML predictions** | ML.NET prediction pipeline runs without errors | Low — compatible packages |
|
||||
|
||||
### 7.4 Behavioral Change Validation
|
||||
|
||||
These specific scenarios require targeted testing due to behavioral changes in .NET 10.0:
|
||||
|
||||
1. **JsonDocument parsing** — Test API response parsing in `RacingApiClient.cs` and `Main.cs`:
|
||||
- Verify JSON responses from FormFav API are parsed correctly
|
||||
- Test edge cases: empty responses, malformed JSON, large payloads
|
||||
|
||||
2. **System.Uri** — Test URL construction in API clients:
|
||||
- Verify API endpoint URLs are constructed correctly
|
||||
- Test WebView2 navigation URLs
|
||||
|
||||
---
|
||||
|
||||
## 8. Risk Management
|
||||
|
||||
### 8.1 Risk Assessment
|
||||
|
||||
| Risk | Level | Description | Mitigation |
|
||||
|---|---|---|---|
|
||||
| SDK-style conversion fails | ?? Medium | Conversion tool may not handle all classic WPF project features | Manual .csproj cleanup after conversion; verify all files included |
|
||||
| SqlClient migration breaks DB access | ?? Medium | Namespace-only change, but large surface area (1,264 references) | Global find-and-replace `System.Data.SqlClient` ? `Microsoft.Data.SqlClient`; API is compatible |
|
||||
| ML.NET packages incompatible | ?? Low | ML packages are pre-release (5.0.0-preview) | Assessment confirms compatibility; fall back to stable ML.NET 4.x if needed |
|
||||
| RestSharp pre-release issues | ?? Low | RestSharp is 112.1.1-alpha.0.4 | Assessment confirms compatibility; fall back to stable RestSharp if needed |
|
||||
| Behavioral changes cause runtime issues | ?? Low | JsonDocument and Uri behavior differences | Configure strict/lenient options; manual testing of affected paths |
|
||||
| Legacy config bridge insufficient | ?? Low | Settings.Designer.cs may need additional adjustments | System.Configuration.ConfigurationManager package provides full bridge |
|
||||
| WPF rendering differences | ?? Low | Minor visual differences between .NET Framework and .NET 10.0 WPF | Visual inspection; typically identical rendering |
|
||||
|
||||
### 8.2 Contingency Plans
|
||||
|
||||
| Scenario | Action |
|
||||
|---|---|
|
||||
| SDK-style conversion produces broken .csproj | Manually create SDK-style .csproj with correct structure, copy settings |
|
||||
| Microsoft.Data.SqlClient has API differences | Check Microsoft.Data.SqlClient migration guide for any breaking changes beyond namespace |
|
||||
| ML.NET predictions fail | Pin ML.NET to last known working version for net10.0 |
|
||||
| Build fails with unresolvable errors | Roll back to `main` branch, investigate specific errors |
|
||||
|
||||
---
|
||||
|
||||
## 9. Complexity & Effort Assessment
|
||||
|
||||
### 9.1 Overall Assessment
|
||||
|
||||
| Factor | Rating | Justification |
|
||||
|---|---|---|
|
||||
| **Overall complexity** | ?? Medium | Single project but large LOC impact (30.6%), major SqlClient migration |
|
||||
| **SDK-style conversion** | Low | Automated tool handles conversion |
|
||||
| **Framework update** | Low | Single TFM change |
|
||||
| **Package updates** | Low | All 9 are simple version bumps (rc ? stable) |
|
||||
| **Package removals** | Low | 6 straightforward removals |
|
||||
| **SqlClient migration** | Medium | Large number of files (16+) but mechanical namespace change |
|
||||
| **WPF compatibility** | Low | Resolved by recompilation |
|
||||
| **Configuration bridge** | Low | Single package addition, 2 affected references |
|
||||
| **Behavioral changes** | Low | 24 references, requires testing not code changes |
|
||||
|
||||
### 9.2 Complexity by File Area
|
||||
|
||||
| Area | Files Affected | Issue Count | Complexity |
|
||||
|---|---|---|---|
|
||||
| Football Database classes | 14 files | ~1,200+ | Medium (bulk SqlClient rename) |
|
||||
| Manager Database classes | 2 files | 22 | Low |
|
||||
| API Client / Main | 2 files | 7 | Low (behavioral testing) |
|
||||
| Settings | 1 file | 2 | Low |
|
||||
| Project file | 1 file | 17 | Low (tooling-assisted) |
|
||||
| WPF UI files | ~8 files | 897 | Low (automatic via recompilation) |
|
||||
|
||||
---
|
||||
|
||||
## 10. Source Control Strategy
|
||||
|
||||
### 10.1 Branch Strategy
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Source branch** | `main` |
|
||||
| **Upgrade branch** | `upgrade-to-NET8` |
|
||||
| **Approach** | Single commit for entire atomic upgrade |
|
||||
|
||||
### 10.2 Commit Strategy
|
||||
|
||||
**Single commit** for the entire All-At-Once upgrade operation:
|
||||
|
||||
- **Commit message**: `Upgrade BettingPredictor from .NET Framework 4.8.1 to .NET 10.0`
|
||||
- **Contents**: SDK-style conversion + TFM change + all package updates + SqlClient migration + config bridge + compilation fixes
|
||||
- **Rationale**: Single project, single atomic operation — one commit captures the complete upgrade
|
||||
|
||||
### 10.3 Review & Merge
|
||||
|
||||
- Create Pull Request from `upgrade-to-NET8` ? `main`
|
||||
- PR checklist:
|
||||
- [ ] Solution builds with 0 errors
|
||||
- [ ] All package references correct (no pre-release, no framework-included)
|
||||
- [ ] SqlClient migration complete (no remaining `System.Data.SqlClient` references)
|
||||
- [ ] Application starts and core functionality works
|
||||
- [ ] Manual verification of behavioral change areas
|
||||
|
||||
---
|
||||
|
||||
## 11. Success Criteria
|
||||
|
||||
### 11.1 Technical Criteria
|
||||
|
||||
- [ ] Project targets `net10.0-windows`
|
||||
- [ ] Project uses SDK-style format
|
||||
- [ ] All 9 packages updated from rc.1 to stable 10.0.5
|
||||
- [ ] All 6 framework-included packages removed
|
||||
- [ ] `Microsoft.Data.SqlClient` added and all `System.Data.SqlClient` references migrated
|
||||
- [ ] `System.Configuration.ConfigurationManager` added for legacy config bridge
|
||||
- [ ] Solution builds with **0 errors**
|
||||
- [ ] No NuGet package dependency conflicts
|
||||
- [ ] No security vulnerabilities
|
||||
|
||||
### 11.2 Quality Criteria
|
||||
|
||||
- [ ] No remaining references to `System.Data.SqlClient` namespace
|
||||
- [ ] No remaining pre-release package versions (except ML.NET and RestSharp which are already pre-release by design)
|
||||
- [ ] No framework-included packages remaining as explicit references
|
||||
- [ ] Code quality maintained (no workarounds or hacks)
|
||||
|
||||
### 11.3 Process Criteria
|
||||
|
||||
- [ ] All-At-Once strategy followed (single atomic operation)
|
||||
- [ ] All changes in `upgrade-to-NET8` branch
|
||||
- [ ] Single commit capturing complete upgrade
|
||||
- [ ] Assessment findings fully addressed
|
||||
@@ -0,0 +1,120 @@
|
||||
# Upgrade Tasks — BettingPredictor (.NET 10.0)
|
||||
|
||||
## Progress Dashboard
|
||||
|
||||
| Status | Count |
|
||||
|---|---|
|
||||
| ? Complete | 1 |
|
||||
| ? In Progress | 0 |
|
||||
| ? Not Started | 6 |
|
||||
**Progress**: 7/7 tasks complete (100%) 
|
||||
| ? Skipped | 0 |
|
||||
| **Total** | **7** |
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
### [?] TASK-001: Verify Prerequisites *(Completed: 2026-03-31 21:10)*
|
||||
**Scope**: Environment readiness
|
||||
**References**: Plan §2.2
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Verify .NET 10.0 SDK is installed on the machine (`dotnet --list-sdks`)
|
||||
- [?] (2) Validate global.json compatibility ? if present, ensure it allows .NET 10.0 SDK; update or remove if needed
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-002: Convert Project to SDK-style *(Completed: 2026-03-31 21:17)*
|
||||
**Scope**: BettingPredictor.csproj
|
||||
**References**: Plan §4.1 Step 1
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Convert `HorseRacingPredictor\HorseRacingPredictor\BettingPredictor.csproj` to SDK-style format using the SDK-style conversion tool
|
||||
- [?] (2) Verify the converted .csproj uses `Microsoft.NET.Sdk` format and all source files are properly included
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-003: Update Target Framework and Project Configuration *(Completed: 2026-03-31 21:23)*
|
||||
**Scope**: BettingPredictor.csproj
|
||||
**References**: Plan §4.1 Step 2
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Set `<TargetFramework>` to `net10.0-windows` in BettingPredictor.csproj
|
||||
- [?] (2) Ensure `<UseWPF>true</UseWPF>` is present in the project file (required for WPF support on .NET 10.0)
|
||||
- [?] (3) Verify project file structure is correct after TFM change
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-004: Update NuGet Package References *(Completed: 2026-03-31 21:32)*
|
||||
**Scope**: BettingPredictor.csproj
|
||||
**References**: Plan §5.1, §5.2, §5.3
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Update 9 pre-release packages to stable 10.0.5:
|
||||
Microsoft.Bcl.AsyncInterfaces, Microsoft.Bcl.Numerics, System.CodeDom,
|
||||
System.Collections.Immutable, System.IO.Pipelines, System.Numerics.Tensors,
|
||||
System.Text.Encodings.Web, System.Text.Json, System.Threading.Channels
|
||||
- [?] (2) Remove 6 framework-included packages:
|
||||
System.Buffers, System.Memory, System.Numerics.Vectors,
|
||||
System.Reflection.Emit.Lightweight, System.Threading.Tasks.Extensions, System.ValueTuple
|
||||
- [?] (3) Add `Microsoft.Data.SqlClient` package (latest stable version)
|
||||
- [?] (4) Add `System.Configuration.ConfigurationManager` package (latest stable for net10.0)
|
||||
- [?] (5) Run `dotnet restore` and verify all packages resolve without conflicts
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-005: Migrate SqlClient Namespace *(Completed: 2026-03-31 21:40)*
|
||||
**Scope**: 16+ source files using System.Data.SqlClient
|
||||
**References**: Plan §6.1, §4.1 Step 5
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) In all files under `Manager\` and `Football\Database\` and `Football\Manager\`, replace:
|
||||
`using System.Data.SqlClient;` ? `using Microsoft.Data.SqlClient;`
|
||||
Affected files (16):
|
||||
- Manager\Database.cs
|
||||
- Football\Manager\Database.cs
|
||||
- Football\Database\APIResponse.cs
|
||||
- Football\Database\LeagueStats.cs
|
||||
- Football\Database\Fixture.cs
|
||||
- Football\Database\Comparison.cs
|
||||
- Football\Database\League.cs
|
||||
- Football\Database\TeamStats.cs
|
||||
- Football\Database\Prediction.cs
|
||||
- Football\Database\Odds.cs
|
||||
- Football\Database\Score.cs
|
||||
- Football\Database\FixtureLeague.cs
|
||||
- Football\Database\H2H.cs
|
||||
- Football\Database\Goals.cs
|
||||
- Football\Database\BetType.cs
|
||||
- Football\Database\Bookmaker.cs
|
||||
- [?] (2) Verify no remaining references to `System.Data.SqlClient` exist in the codebase
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-006: Build Solution and Fix Compilation Errors *(Completed: 2026-03-31 21:48)*
|
||||
**Scope**: Entire solution
|
||||
**References**: Plan §4.1 Step 7
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Build the entire solution
|
||||
- [?] (2) Fix any compilation errors discovered during build (expected areas: SqlClient namespace, removed APIs, configuration, WPF assembly references)
|
||||
- [?] (3) Rebuild and verify: **0 compilation errors**
|
||||
|
||||
---
|
||||
|
||||
### [?] TASK-007: Final Verification and Commit *(Completed: 2026-03-31 21:55)*
|
||||
**Scope**: Entire solution
|
||||
**References**: Plan §10, §11
|
||||
|
||||
**Actions:**
|
||||
- [?] (1) Verify all success criteria from Plan ?11:
|
||||
- Project targets net10.0-windows
|
||||
- Project uses SDK-style format
|
||||
- All 9 packages updated to stable versions
|
||||
- 6 framework-included packages removed
|
||||
- Microsoft.Data.SqlClient added and usages migrated
|
||||
- System.Configuration.ConfigurationManager added
|
||||
- Solution builds with 0 errors
|
||||
- [?] (2) Stage and commit all changes:
|
||||
Message: `Upgrade BettingPredictor from .NET Framework 4.8.1 to .NET 10.0`
|
||||
@@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<!-- System.Threading.Tasks.Extensions -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<!-- System.Memory -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<!-- System.Runtime.CompilerServices.Unsafe -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<!-- System.Buffers -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<!-- System.Numerics.Vectors -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.6.0" newVersion="4.1.6.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<!-- Microsoft.Bcl.AsyncInterfaces -->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bcl.HashCode" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Numerics.Tensors" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -1,219 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Microsoft.ML.FastTree.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.FastTree.props" Condition="Exists('..\packages\Microsoft.ML.FastTree.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.FastTree.props')" />
|
||||
<Import Project="..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.props" Condition="Exists('..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.props')" />
|
||||
<Import Project="..\packages\Microsoft.ML.CpuMath.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.CpuMath.props" Condition="Exists('..\packages\Microsoft.ML.CpuMath.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.CpuMath.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{63138155-B7F3-4246-B47B-B8CC2D7A60A4}</ProjectGuid>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>HorseRacingPredictor</RootNamespace>
|
||||
<AssemblyName>HorseRacingPredictor</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CsvHelper, Version=33.0.0.0, Culture=neutral, PublicKeyToken=8c4a6d608ce8f59c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CsvHelper.33.1.0\lib\net48\CsvHelper.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.3800.47, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.3800.47, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.WinForms, Version=1.0.3800.47, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RestSharp, Version=112.1.1.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RestSharp.112.1.1-alpha.0.4\lib\net48\RestSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.CodeDom, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.CodeDom.10.0.0-rc.1.25451.107\lib\net462\System.CodeDom.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.10.0.0-rc.1.25451.107\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Pipelines, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Pipelines.10.0.0-rc.1.25451.107\lib\net462\System.IO.Pipelines.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Tensors, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Tensors.10.0.0-rc.1.25451.107\lib\net462\System.Numerics.Tensors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.Encodings.Web, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Encodings.Web.10.0.0-rc.1.25451.107\lib\net462\System.Text.Encodings.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Json.10.0.0-rc.1.25451.107\lib\net462\System.Text.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Channels, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Channels.10.0.0-rc.1.25451.107\lib\net462\System.Threading.Channels.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="11.0.0-preview.2.26159.112" />
|
||||
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Bcl.Numerics" Version="11.0.0-preview.2.26159.112" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.ML" Version="6.0.0-preview.26160.2" />
|
||||
<PackageReference Include="Microsoft.ML.CpuMath" Version="6.0.0-preview.26160.2" />
|
||||
<PackageReference Include="Microsoft.ML.DataView" Version="6.0.0-preview.26160.2" />
|
||||
<PackageReference Include="Microsoft.ML.FastTree" Version="6.0.0-preview.26160.2" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3908-prerelease" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.5-beta1" />
|
||||
<PackageReference Include="RestSharp" Version="114.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.5" />
|
||||
<PackageReference Include="System.Numerics.Tensors" Version="11.0.0-preview.2.26159.112" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="VirtualFootball\VirtualMatch.cs" />
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CsvHelperStubs.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Football\Database\BetType.cs" />
|
||||
<Compile Include="Football\Database\Bookmaker.cs" />
|
||||
<Compile Include="Football\Database\APIResponse.cs" />
|
||||
<Compile Include="Football\Database\TeamStats.cs" />
|
||||
<Compile Include="Football\Database\LeagueStats.cs" />
|
||||
<Compile Include="Football\Database\H2H.cs" />
|
||||
<Compile Include="Football\Database\Comparison.cs" />
|
||||
<Compile Include="Football\Database\FixtureLeague.cs" />
|
||||
<Compile Include="Football\Main.cs" />
|
||||
<Compile Include="Football\Manager\API.cs" />
|
||||
<Compile Include="Football\API\Fixture.cs" />
|
||||
<Compile Include="Football\API\League.cs" />
|
||||
<Compile Include="Football\API\Odds.cs" />
|
||||
<Compile Include="Football\API\Prediction.cs" />
|
||||
<Compile Include="Football\Database\Fixture.cs" />
|
||||
<Compile Include="Football\Database\Goals.cs" />
|
||||
<Compile Include="Football\Database\League.cs" />
|
||||
<Compile Include="Football\Database\Odds.cs" />
|
||||
<Compile Include="Football\Database\Prediction.cs" />
|
||||
<Compile Include="Football\Database\Score.cs" />
|
||||
<Compile Include="Football\Database\Team.cs" />
|
||||
<Compile Include="HorseRacing\API\RacingApiClient.cs" />
|
||||
<Compile Include="HorseRacing\Main.cs" />
|
||||
<Compile Include="Horses\Calculator.cs" />
|
||||
<Compile Include="Horses\Files\Maps\Punters.cs" />
|
||||
<Compile Include="Manager\API.cs" />
|
||||
<Compile Include="Manager\FileReader.cs" />
|
||||
<Compile Include="Manager\Database.cs" />
|
||||
<Compile Include="Football\Manager\Database.cs" />
|
||||
<Compile Include="Horses\FileReader.cs" />
|
||||
<Compile Include="Horses\Database.cs" />
|
||||
<Compile Include="Horses\Files\Punters.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<ItemGroup Label="Compile items now included by globbing that were not in the original project file">
|
||||
<Compile Remove="UI\NavButton.cs" />
|
||||
<Compile Remove="UI\ModernTheme.cs" />
|
||||
<Compile Remove="UI\ModernProgressBar.cs" />
|
||||
<Compile Remove="UI\ModernButton.cs" />
|
||||
<Compile Remove="UI\Controls\ModernTextBox.cs" />
|
||||
<Compile Remove="UI\Controls\ModernTabControl.cs" />
|
||||
<Compile Remove="UI\Controls\ModernProgressBar.cs" />
|
||||
<Compile Remove="UI\Controls\ModernPanel.cs" />
|
||||
<Compile Remove="UI\Controls\ModernLabel.cs" />
|
||||
<Compile Remove="UI\Controls\ModernDateTimePicker.cs" />
|
||||
<Compile Remove="UI\Controls\ModernDataGridView.cs" />
|
||||
<Compile Remove="UI\Controls\ModernButton.cs" />
|
||||
<Compile Remove="UI\CardPanel.cs" />
|
||||
<Compile Remove="Program.cs" />
|
||||
<Compile Remove="Main.Designer.cs" />
|
||||
<Compile Remove="Main.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<ItemGroup Label="EmbeddedResource items now included by globbing that were not in the original project file">
|
||||
<EmbeddedResource Remove="Main.resx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Themes\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>Questo progetto fa riferimento a uno o pi� pacchetti NuGet che non sono presenti in questo computer. Usare lo strumento di ripristino dei pacchetti NuGet per scaricarli. Per altre informazioni, vedere http://go.microsoft.com/fwlink/?LinkID=322105. Il file mancante � {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.ML.CpuMath.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.CpuMath.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.CpuMath.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.CpuMath.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.ML.FastTree.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.FastTree.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.FastTree.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.FastTree.props'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.3800.47\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.3800.47\build\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.targets" Condition="Exists('..\packages\Microsoft.ML.5.0.0-preview.25503.2\build\netstandard2.0\Microsoft.ML.targets')" />
|
||||
</Project>
|
||||
@@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
// Minimal compatibility stubs for CsvHelper types used in the project.
|
||||
// These are light-weight placeholders so the project can compile when the CsvHelper NuGet
|
||||
// package is not restored. They intentionally provide only the members the project expects.
|
||||
|
||||
namespace CsvHelper.Configuration.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
|
||||
public sealed class NameAttribute : Attribute
|
||||
{
|
||||
public string[] Names { get; }
|
||||
public NameAttribute(params string[] names) => Names = names ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
|
||||
public sealed class TypeConverterAttribute : Attribute
|
||||
{
|
||||
public Type ConverterType { get; }
|
||||
public TypeConverterAttribute(Type converterType) => ConverterType = converterType;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CsvHelper.TypeConversion
|
||||
{
|
||||
public class DefaultTypeConverter
|
||||
{
|
||||
// Signature matches CsvHelper's DefaultTypeConverter override used in the project
|
||||
public virtual object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData) => (object)text;
|
||||
}
|
||||
|
||||
public class TypeConverterCache
|
||||
{
|
||||
private readonly Dictionary<Type, object> _converters = new Dictionary<Type, object>();
|
||||
public void AddConverter<T>(DefaultTypeConverter converter) => _converters[typeof(T)] = converter;
|
||||
}
|
||||
|
||||
// Minimal placeholders referenced in code
|
||||
public interface IReaderRow { }
|
||||
public class MemberMapData { }
|
||||
}
|
||||
|
||||
namespace CsvHelper.Configuration
|
||||
{
|
||||
public enum TrimOptions { None, Trim }
|
||||
|
||||
public class CsvConfiguration
|
||||
{
|
||||
public CsvConfiguration(CultureInfo culture) { Culture = culture; }
|
||||
public CultureInfo Culture { get; }
|
||||
public bool HasHeaderRecord { get; set; }
|
||||
public Func<PrepareHeaderForMatchArgs, string> PrepareHeaderForMatch { get; set; }
|
||||
public object HeaderValidated { get; set; }
|
||||
public Action<object> MissingFieldFound { get; set; }
|
||||
public Action<object> BadDataFound { get; set; }
|
||||
public TrimOptions TrimOptions { get; set; }
|
||||
public string Delimiter { get; set; }
|
||||
}
|
||||
|
||||
// Basic ClassMap and mapping helper used by the project's mapping files
|
||||
public class ClassMap<T>
|
||||
{
|
||||
public MemberMap Map(Func<T, object> func) => new MemberMap();
|
||||
}
|
||||
|
||||
public class MemberMap
|
||||
{
|
||||
public MemberMap Name(params string[] names) { return this; }
|
||||
}
|
||||
|
||||
// Minimal args used by PrepareHeaderForMatch delegate in code above
|
||||
public class PrepareHeaderForMatchArgs
|
||||
{
|
||||
public string Header { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
namespace CsvHelper
|
||||
{
|
||||
using CsvHelper.Configuration;
|
||||
using CsvHelper.TypeConversion;
|
||||
|
||||
public class CsvReader : IDisposable
|
||||
{
|
||||
private readonly TextReader _reader;
|
||||
public CsvReader(TextReader reader, CsvConfiguration config)
|
||||
{
|
||||
_reader = reader;
|
||||
Configuration = config;
|
||||
Context = new ReaderContext();
|
||||
}
|
||||
|
||||
public CsvConfiguration Configuration { get; }
|
||||
public ReaderContext Context { get; }
|
||||
|
||||
public void Dispose() { /* nothing to dispose in stub */ }
|
||||
|
||||
public bool Read() => false;
|
||||
public void ReadHeader() { }
|
||||
public string[] HeaderRecord => Array.Empty<string>();
|
||||
public string GetField(int index) => null;
|
||||
|
||||
public IEnumerable<T> GetRecords<T>() { return Enumerable.Empty<T>(); }
|
||||
|
||||
public class ReaderContext
|
||||
{
|
||||
public TypeConverterCache TypeConverterCache { get; } = new TypeConverterCache();
|
||||
public void RegisterClassMap<TMap>() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Text;
|
||||
using RestSharp;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
@@ -11,7 +11,7 @@ namespace HorseRacingPredictor.Football.Database
|
||||
try
|
||||
{
|
||||
var id = betType["id"]?.Value<int>();
|
||||
if (id == null) return; // Salta il record se l'id è null
|
||||
if (id == null) return; // Salta il record se l'id è null
|
||||
|
||||
var query = @"
|
||||
IF EXISTS (SELECT 1 FROM BetType WHERE bet_type_id = @bet_type_id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
@@ -11,7 +11,7 @@ namespace HorseRacingPredictor.Football.Database
|
||||
try
|
||||
{
|
||||
var id = bookmaker["id"]?.Value<int>();
|
||||
if (id == null) return; // Salta il record se l'id è null
|
||||
if (id == null) return; // Salta il record se l'id è null
|
||||
|
||||
var query = @"
|
||||
IF EXISTS (SELECT 1 FROM Bookmaker WHERE bookmaker_id = @bookmaker_id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
@@ -143,7 +143,7 @@ namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
try
|
||||
{
|
||||
using (var connection = new System.Data.SqlClient.SqlConnection(_connectionString))
|
||||
using (var connection = new Microsoft.Data.SqlClient.SqlConnection(_connectionString))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace HorseRacingPredictor.Football.Database
|
||||
ORDER BY
|
||||
f.date ASC";
|
||||
|
||||
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(query, connection))
|
||||
using (var adapter = new Microsoft.Data.SqlClient.SqlDataAdapter(query, connection))
|
||||
{
|
||||
adapter.Fill(result);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HorseRacingPredictor.Football.Database
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
@@ -8,7 +8,7 @@ using RestSharp;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace HorseRacingPredictor.Football
|
||||
{
|
||||
@@ -276,7 +276,7 @@ namespace HorseRacingPredictor.Football
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera le quote per la data specificata (potenzialmente più pagine) utilizzando API.Odds
|
||||
/// Recupera le quote per la data specificata (potenzialmente più pagine) utilizzando API.Odds
|
||||
/// </summary>
|
||||
private List<RestResponse> GetOdds(DateTime date)
|
||||
{
|
||||
@@ -392,7 +392,7 @@ namespace HorseRacingPredictor.Football
|
||||
}
|
||||
}
|
||||
|
||||
// FASE 5: Inserisci relazioni tra entità e dati dipendenti
|
||||
// FASE 5: Inserisci relazioni tra entità e dati dipendenti
|
||||
foreach (var responseItem in asArray(jsonObject["response"]))
|
||||
{
|
||||
int? fixtureId = null;
|
||||
@@ -572,8 +572,8 @@ namespace HorseRacingPredictor.Football
|
||||
{
|
||||
try
|
||||
{
|
||||
// In questo metodo non elaboriamo più direttamente le risposte
|
||||
// Le risposte sono già state salvate nel database dalla classe API
|
||||
// In questo metodo non elaboriamo più direttamente le risposte
|
||||
// Le risposte sono già state salvate nel database dalla classe API
|
||||
// e verranno elaborate dal metodo ProcessUnprocessedApiResponses
|
||||
|
||||
// Processa le risposte non elaborate
|
||||
@@ -608,7 +608,7 @@ namespace HorseRacingPredictor.Football
|
||||
dataTable.Columns.Add("Quota Trasferta", typeof(string));
|
||||
dataTable.Columns.Add("Over 2.5", typeof(string));
|
||||
dataTable.Columns.Add("Under 2.5", typeof(string));
|
||||
dataTable.Columns.Add("BTTS Sì", typeof(string));
|
||||
dataTable.Columns.Add("BTTS Sì", typeof(string));
|
||||
dataTable.Columns.Add("BTTS No", typeof(string));
|
||||
dataTable.Columns.Add("Doppia Casa/X", typeof(string));
|
||||
dataTable.Columns.Add("Doppia Casa/Trasf", typeof(string));
|
||||
@@ -646,7 +646,7 @@ namespace HorseRacingPredictor.Football
|
||||
{
|
||||
try
|
||||
{
|
||||
// Verifica che le proprietà essenziali esistano
|
||||
// Verifica che le proprietà essenziali esistano
|
||||
if (!item.TryGetProperty("fixture", out var fixtureEl) ||
|
||||
!item.TryGetProperty("league", out var leagueEl) ||
|
||||
!item.TryGetProperty("teams", out var teamsEl))
|
||||
@@ -700,7 +700,7 @@ namespace HorseRacingPredictor.Football
|
||||
row["Quota Trasferta"] = DBNull.Value;
|
||||
row["Over 2.5"] = DBNull.Value;
|
||||
row["Under 2.5"] = DBNull.Value;
|
||||
row["BTTS Sì"] = DBNull.Value;
|
||||
row["BTTS Sì"] = DBNull.Value;
|
||||
row["BTTS No"] = DBNull.Value;
|
||||
row["Doppia Casa/X"] = DBNull.Value;
|
||||
row["Doppia Casa/Trasf"] = DBNull.Value;
|
||||
@@ -731,7 +731,7 @@ namespace HorseRacingPredictor.Football
|
||||
// Crea una copia del DataTable delle partite
|
||||
var combinedTable = fixturesTable.Copy();
|
||||
|
||||
// Se non ci sono risposte di quote o la tabella delle partite è vuota, ritorna la tabella originale
|
||||
// Se non ci sono risposte di quote o la tabella delle partite è vuota, ritorna la tabella originale
|
||||
if (oddsResponses == null || oddsResponses.Count == 0 || combinedTable.Rows.Count == 0)
|
||||
{
|
||||
return combinedTable;
|
||||
@@ -868,7 +868,7 @@ namespace HorseRacingPredictor.Football
|
||||
var table = new DataTable();
|
||||
table.Columns.Add("Errore", typeof(string));
|
||||
var row = table.NewRow();
|
||||
row["Errore"] = "Si è verificato un errore durante il recupero dei dati.";
|
||||
row["Errore"] = "Si è verificato un errore durante il recupero dei dati.";
|
||||
table.Rows.Add(row);
|
||||
return table;
|
||||
}
|
||||
@@ -972,7 +972,7 @@ namespace HorseRacingPredictor.Football
|
||||
{
|
||||
string val = GetOddValueString(v, "value");
|
||||
string odd = GetOddValueString(v, "odd");
|
||||
if (val == "Yes") row["BTTS Sì"] = odd;
|
||||
if (val == "Yes") row["BTTS Sì"] = odd;
|
||||
else if (val == "No") row["BTTS No"] = odd;
|
||||
}
|
||||
break;
|
||||
@@ -1079,7 +1079,7 @@ namespace HorseRacingPredictor.Football
|
||||
var result = CreateEmptyFixturesDataTable();
|
||||
|
||||
// Step 2: Delegare alla classe repository appropriata il recupero dei dati
|
||||
// Utilizziamo principalmente il repository Fixture che può coordinarsi con gli altri repository
|
||||
// Utilizziamo principalmente il repository Fixture che può coordinarsi con gli altri repository
|
||||
return _fixtureRepository.GetProcessedFixtures();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -9,12 +9,12 @@ namespace HorseRacingPredictor.Football.Manager
|
||||
{
|
||||
internal class API : HorseRacingPredictor.Manager.API
|
||||
{
|
||||
// Configurazione dell'API
|
||||
protected const string ApiKey = "f3795ccef056c5478d316162517d9970";
|
||||
protected const string KeyHeader = "x-rapidapi-key";
|
||||
// Configurazione dell'API – caricata da appsettings.json
|
||||
protected static string ApiKey => AppConfig.FootballApiKey;
|
||||
protected static string KeyHeader => AppConfig.FootballApiKeyHeader;
|
||||
protected const string HostHeader = "x-rapidapi-host";
|
||||
protected const string HostValue = "v3.football.api-sports.io";
|
||||
protected const string BaseUrl = "https://v3.football.api-sports.io";
|
||||
protected static string HostValue => AppConfig.FootballApiHost;
|
||||
protected static string BaseUrl => $"https://{AppConfig.FootballApiHost}";
|
||||
|
||||
// Repository per le risposte API
|
||||
private readonly APIResponse _apiResponseRepository;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Text.Json.Nodes;
|
||||
using HorseRacingPredictor.Football.Database;
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace HorseRacingPredictor.Football.Manager
|
||||
{
|
||||
internal class Database : HorseRacingPredictor.Manager.Database
|
||||
{
|
||||
// Implementazione della proprietà astratta _connectionString per il database calcio
|
||||
protected override string _connectionString => "Server=DESKTOP-9O9JHFS;Database=TestBS_Football;User Id=sa;Password=Asti2019;";
|
||||
public Database() : base(AppConfig.FootballConnectionString) { }
|
||||
|
||||
// Usato il modificatore "new" per evitare il warning CS0108
|
||||
protected new void LogError(string operation, Exception ex)
|
||||
@@ -28,11 +27,11 @@ namespace HorseRacingPredictor.Football.Manager
|
||||
base.ExecuteTransactionalQuery(operation, action);
|
||||
}
|
||||
|
||||
// Questo metodo ora è vuoto e sarà implementato nella Main
|
||||
// Questo metodo ora è vuoto e sarà implementato nella Main
|
||||
public void ProcessAndInsertData(string jsonResponse)
|
||||
{
|
||||
// Il codice è stato spostato in Football.Main
|
||||
LogError("l'elaborazione dei dati calcistici", new NotImplementedException("Questo metodo è stato spostato in Football.Main"));
|
||||
// Il codice è stato spostato in Football.Main
|
||||
LogError("l'elaborazione dei dati calcistici", new NotImplementedException("Questo metodo è stato spostato in Football.Main"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,7 +31,9 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
/// Esegue una richiesta GET autenticata con X-API-Key header.
|
||||
/// Rispetta un intervallo minimo tra richieste e gestisce HTTP 429 con retry.
|
||||
/// </summary>
|
||||
private RestResponse ExecuteRequest(string endpoint, CancellationToken ct = default)
|
||||
/// <param name="throwOnNotFound">Se false, restituisce null in caso di 404.</param>
|
||||
private RestResponse ExecuteRequest(string endpoint, CancellationToken ct = default,
|
||||
bool throwOnNotFound = true)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -67,7 +69,7 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
{
|
||||
var response = client.Execute(request);
|
||||
|
||||
// HTTP 429 Too Many Requests – backoff e riprova
|
||||
// HTTP 429 Too Many Requests - backoff e riprova
|
||||
if (response.StatusCode == (HttpStatusCode)429 && attempt <= MaxRetries)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
@@ -81,6 +83,10 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
continue;
|
||||
}
|
||||
|
||||
// 404 Not Found - restituisci null se richiesto
|
||||
if (response.StatusCode == HttpStatusCode.NotFound && !throwOnNotFound)
|
||||
return null;
|
||||
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
throw new Exception(
|
||||
@@ -98,10 +104,10 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene l'elenco dei meeting per una data
|
||||
/// Ottiene l'elenco dei meeting per una data (solo meeting australiani).
|
||||
/// </summary>
|
||||
public RestResponse GetMeetings(DateTime date, string raceCode = "gallops",
|
||||
string timezone = "Europe/Rome", CancellationToken ct = default)
|
||||
string timezone = "Australia/Sydney", CancellationToken ct = default)
|
||||
{
|
||||
var sb = new StringBuilder("form/meetings?");
|
||||
sb.Append($"date={date:yyyy-MM-dd}");
|
||||
@@ -114,11 +120,38 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene i dati di forma per una singola corsa
|
||||
/// Ottiene i dati di forma per una singola corsa.
|
||||
/// Lancia eccezione se la corsa non esiste.
|
||||
/// </summary>
|
||||
public RestResponse GetRaceForm(DateTime date, string track, int raceNumber,
|
||||
string raceCode = "gallops", string country = "au",
|
||||
string timezone = "Europe/Rome", CancellationToken ct = default)
|
||||
string timezone = "Australia/Sydney", CancellationToken ct = default)
|
||||
{
|
||||
string endpoint = BuildFormEndpoint(date, track, raceNumber, raceCode, country, timezone);
|
||||
return ExecuteRequest(endpoint, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prova a ottenere i dati di forma per una corsa. Restituisce null se 404.
|
||||
/// </summary>
|
||||
public RestResponse TryGetRaceForm(DateTime date, string track, int raceNumber,
|
||||
string raceCode = "gallops", string country = "au",
|
||||
string timezone = "Australia/Sydney", CancellationToken ct = default)
|
||||
{
|
||||
string endpoint = BuildFormEndpoint(date, track, raceNumber, raceCode, country, timezone);
|
||||
return ExecuteRequest(endpoint, ct, throwOnNotFound: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene l'elenco delle piste/venue disponibili.
|
||||
/// </summary>
|
||||
public RestResponse GetVenues(CancellationToken ct = default)
|
||||
{
|
||||
return ExecuteRequest("form/venues", ct);
|
||||
}
|
||||
|
||||
private static string BuildFormEndpoint(DateTime date, string track, int raceNumber,
|
||||
string raceCode, string country, string timezone)
|
||||
{
|
||||
var sb = new StringBuilder("form?");
|
||||
sb.Append($"date={date:yyyy-MM-dd}");
|
||||
@@ -130,16 +163,7 @@ namespace HorseRacingPredictor.HorseRacing.API
|
||||
sb.Append($"&country={country}");
|
||||
if (!string.IsNullOrEmpty(timezone))
|
||||
sb.Append($"&timezone={Uri.EscapeDataString(timezone)}");
|
||||
|
||||
return ExecuteRequest(sb.ToString(), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene l'elenco delle piste/venue disponibili
|
||||
/// </summary>
|
||||
public RestResponse GetVenues(CancellationToken ct = default)
|
||||
{
|
||||
return ExecuteRequest("form/venues", ct);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using HorseRacingPredictor.HorseRacing.API;
|
||||
@@ -10,30 +11,66 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
/// <summary>
|
||||
/// Gestore centralizzato per la sezione Corse dei Cavalli.
|
||||
/// Scarica i dati da FormFav Racing API e li converte in DataTable.
|
||||
///
|
||||
/// NOTA: l'API FormFav supporta dati di forma SOLO per AU e NZ.
|
||||
/// - AU: discovery efficiente tramite /form/meetings
|
||||
/// - NZ: discovery tramite probing delle venue (meetings restituisce solo AU)
|
||||
/// Le altre nazioni (gb, ie, fr, ...) sono presenti nel catalogo venue
|
||||
/// ma non hanno dati di forma disponibili.
|
||||
/// </summary>
|
||||
public class Main
|
||||
{
|
||||
/// <summary>Nazioni con dati di forma disponibili nell'API.</summary>
|
||||
public static readonly string[] SupportedCountries = { "au", "nz" };
|
||||
|
||||
/// <summary>Tutte le nazioni nel catalogo venue (solo AU e NZ hanno form data).</summary>
|
||||
public static readonly string[] AllCountries = {
|
||||
"au","nz","hk","gb","ie","fr","us","ca","jp","sg",
|
||||
"ae","sa","za","de","it","se","no","dk","kr","my",
|
||||
"ar","br","cl","ma"
|
||||
};
|
||||
|
||||
/// <summary>Nomi leggibili per ogni codice nazione.</summary>
|
||||
public static readonly Dictionary<string, string> CountryNames = new Dictionary<string, string>
|
||||
{
|
||||
{"au","Australia"},{"nz","Nuova Zelanda"},{"hk","Hong Kong"},
|
||||
{"gb","Gran Bretagna"},{"ie","Irlanda"},{"fr","Francia"},
|
||||
{"us","Stati Uniti"},{"ca","Canada"},{"jp","Giappone"},
|
||||
{"sg","Singapore"},{"ae","Emirati Arabi"},{"sa","Arabia Saudita"},
|
||||
{"za","Sudafrica"},{"de","Germania"},{"it","Italia"},
|
||||
{"se","Svezia"},{"no","Norvegia"},{"dk","Danimarca"},
|
||||
{"kr","Corea del Sud"},{"my","Malesia"},{"ar","Argentina"},
|
||||
{"br","Brasile"},{"cl","Cile"},{"ma","Marocco"}
|
||||
};
|
||||
|
||||
private const int MaxRacesPerVenue = 12;
|
||||
|
||||
private RacingApiClient _client;
|
||||
private List<VenueInfo> _venuesCache;
|
||||
|
||||
/// <summary>Nazioni da scaricare (default: au, nz). Solo au e nz sono supportate.</summary>
|
||||
public List<string> Countries { get; set; } = new List<string> { "au", "nz" };
|
||||
|
||||
/// <summary>Timezone IANA (default: Australia/Sydney).</summary>
|
||||
public string Timezone { get; set; } = "Australia/Sydney";
|
||||
|
||||
public Main(string apiKey)
|
||||
{
|
||||
_client = new RacingApiClient(apiKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorna la API key
|
||||
/// </summary>
|
||||
public void UpdateApiKey(string apiKey)
|
||||
{
|
||||
_client = new RacingApiClient(apiKey);
|
||||
_venuesCache = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scarica tutti i meeting per una data, poi per ciascun meeting scarica tutte le corse
|
||||
/// e le restituisce come DataTable con una riga per runner.
|
||||
/// La progress bar avanza per singola corsa scaricata.
|
||||
/// Scarica tutte le corse per una data.
|
||||
/// - Per AU: usa /form/meetings (efficiente, restituisce numero corse)
|
||||
/// - Per NZ: usa probing venue per venue
|
||||
/// </summary>
|
||||
public DataTable GetAllRacesForDate(DateTime date, string raceCode = "gallops",
|
||||
public DataTable GetAllRacesForDate(DateTime date,
|
||||
IProgress<int> progress = null, IProgress<string> status = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
@@ -41,75 +78,226 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
|
||||
try
|
||||
{
|
||||
status?.Report("Connessione a FormFav API...");
|
||||
progress?.Report(2);
|
||||
// Filtra solo nazioni supportate
|
||||
var requestedCountries = Countries
|
||||
.Select(c => c.ToLowerInvariant())
|
||||
.Where(c => SupportedCountries.Contains(c))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// 1. Ottieni l'elenco dei meeting per la data
|
||||
var meetingsResp = _client.GetMeetings(date, raceCode, ct: ct);
|
||||
progress?.Report(8);
|
||||
|
||||
var meetings = ParseMeetings(meetingsResp.Content);
|
||||
if (meetings.Count == 0)
|
||||
if (requestedCountries.Count == 0)
|
||||
{
|
||||
status?.Report("Nessun meeting trovato per questa data");
|
||||
status?.Report("Nessuna nazione supportata selezionata. Usa: AU, NZ");
|
||||
progress?.Report(100);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// 2. Calcola il totale corse per un progresso granulare
|
||||
int totalRaces = 0;
|
||||
foreach (var m in meetings)
|
||||
if (!m.Abandoned) totalRaces += m.NumberOfRaces;
|
||||
bool doAu = requestedCountries.Contains("au");
|
||||
bool doNz = requestedCountries.Contains("nz");
|
||||
|
||||
if (totalRaces == 0)
|
||||
int totalPhases = (doAu ? 1 : 0) + (doNz ? 1 : 0);
|
||||
int currentPhase = 0;
|
||||
int totalRunners = 0;
|
||||
int totalErrors = 0;
|
||||
int totalMeetings = 0;
|
||||
|
||||
// ?? FASE AU: usa /form/meetings ??
|
||||
if (doAu)
|
||||
{
|
||||
status?.Report("Tutti i meeting sono stati annullati");
|
||||
progress?.Report(100);
|
||||
return dt;
|
||||
}
|
||||
status?.Report("AU: Recupero elenco meeting...");
|
||||
progress?.Report(2);
|
||||
|
||||
status?.Report($"Trovati {meetings.Count} meeting ({totalRaces} corse). Scaricamento...");
|
||||
int completedRaces = 0;
|
||||
int errors = 0;
|
||||
int phaseBase = 0;
|
||||
int phaseSpan = doNz ? 50 : 95; // Se c'e' anche NZ, AU occupa 0-50%
|
||||
|
||||
// 3. Scarica ogni singola corsa
|
||||
foreach (var meeting in meetings)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (meeting.Abandoned) continue;
|
||||
|
||||
for (int raceNum = 1; raceNum <= meeting.NumberOfRaces; raceNum++)
|
||||
try
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
var meetingsResp = _client.GetMeetings(date, "gallops", Timezone, ct);
|
||||
var meetings = ParseMeetings(meetingsResp.Content);
|
||||
|
||||
try
|
||||
if (meetings.Count > 0)
|
||||
{
|
||||
status?.Report($"{meeting.Track} R{raceNum}/{meeting.NumberOfRaces} " +
|
||||
$"({completedRaces + 1}/{totalRaces})");
|
||||
// Calcola totale corse AU
|
||||
int totalAuRaces = 0;
|
||||
foreach (var m in meetings)
|
||||
if (!m.Abandoned) totalAuRaces += m.NumberOfRaces;
|
||||
|
||||
var formResp = _client.GetRaceForm(date, meeting.TrackSlug, raceNum,
|
||||
raceCode, meeting.Country, ct: ct);
|
||||
status?.Report($"AU: {meetings.Count} meeting ({totalAuRaces} corse)");
|
||||
|
||||
ParseRaceFormIntoTable(dt, formResp.Content);
|
||||
int completedAuRaces = 0;
|
||||
|
||||
foreach (var meeting in meetings)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (meeting.Abandoned) continue;
|
||||
|
||||
totalMeetings++;
|
||||
|
||||
for (int raceNum = 1; raceNum <= meeting.NumberOfRaces; raceNum++)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
status?.Report($"AU: {meeting.Track} R{raceNum}/{meeting.NumberOfRaces} " +
|
||||
$"({completedAuRaces + 1}/{totalAuRaces})");
|
||||
|
||||
try
|
||||
{
|
||||
var formResp = _client.GetRaceForm(date, meeting.TrackSlug,
|
||||
raceNum, "gallops", "au", Timezone, ct);
|
||||
ParseRaceFormIntoTable(dt, formResp.Content, "au");
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
totalErrors++;
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"Errore AU {meeting.Track} R{raceNum}: {ex.Message}");
|
||||
}
|
||||
|
||||
completedAuRaces++;
|
||||
int pct = phaseBase + (int)((double)completedAuRaces / Math.Max(totalAuRaces, 1) * phaseSpan);
|
||||
progress?.Report(Math.Min(pct, phaseBase + phaseSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
errors++;
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"Errore scaricamento {meeting.Track} R{raceNum}: {ex.Message}");
|
||||
status?.Report("AU: Nessun meeting trovato");
|
||||
}
|
||||
|
||||
completedRaces++;
|
||||
// Progresso: 8% per meetings, 8-98% per le corse singole, 100% alla fine
|
||||
int pct = 8 + (int)((double)completedRaces / totalRaces * 90);
|
||||
progress?.Report(Math.Min(pct, 98));
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
totalErrors++;
|
||||
System.Diagnostics.Debug.WriteLine($"Errore fase AU meetings: {ex.Message}");
|
||||
}
|
||||
|
||||
currentPhase++;
|
||||
}
|
||||
|
||||
// ?? FASE NZ: probing venue per venue ??
|
||||
if (doNz)
|
||||
{
|
||||
int phaseBase = doAu ? 50 : 0;
|
||||
int phaseSpan = doAu ? 48 : 95;
|
||||
|
||||
status?.Report("NZ: Caricamento elenco piste...");
|
||||
progress?.Report(phaseBase + 2);
|
||||
|
||||
try
|
||||
{
|
||||
var nzVenues = GetFilteredVenues("nz", ct);
|
||||
|
||||
if (nzVenues.Count > 0)
|
||||
{
|
||||
// Discovery: prova Race 1 per ogni venue
|
||||
int venuesChecked = 0;
|
||||
var activeVenues = new List<ActiveVenue>();
|
||||
int discoverySpan = phaseSpan / 3;
|
||||
|
||||
foreach (var v in nzVenues)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
status?.Report($"NZ: Verifica {v.Name}... [{venuesChecked + 1}/{nzVenues.Count}]");
|
||||
|
||||
try
|
||||
{
|
||||
var resp = _client.TryGetRaceForm(date, v.Slug, 1,
|
||||
"gallops", "nz", Timezone, ct);
|
||||
|
||||
if (resp != null && !string.IsNullOrEmpty(resp.Content))
|
||||
{
|
||||
activeVenues.Add(new ActiveVenue
|
||||
{
|
||||
Name = v.Name,
|
||||
Slug = v.Slug,
|
||||
Country = "nz",
|
||||
FirstRaceContent = resp.Content
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch { }
|
||||
|
||||
venuesChecked++;
|
||||
int pct = phaseBase + (int)((double)venuesChecked / nzVenues.Count * discoverySpan);
|
||||
progress?.Report(Math.Min(pct, phaseBase + discoverySpan));
|
||||
}
|
||||
|
||||
// Download rimanenti corse per venue attive
|
||||
if (activeVenues.Count > 0)
|
||||
{
|
||||
int downloadBase = phaseBase + discoverySpan;
|
||||
int downloadSpan = phaseSpan - discoverySpan;
|
||||
int completedNzRaces = 0;
|
||||
int estimatedNzRaces = activeVenues.Count * 8;
|
||||
|
||||
status?.Report($"NZ: {activeVenues.Count} meeting attivi");
|
||||
|
||||
foreach (var av in activeVenues)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
totalMeetings++;
|
||||
|
||||
// Parsifica Race 1 (gia' scaricata)
|
||||
ParseRaceFormIntoTable(dt, av.FirstRaceContent, "nz");
|
||||
completedNzRaces++;
|
||||
|
||||
for (int raceNum = 2; raceNum <= MaxRacesPerVenue; raceNum++)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
status?.Report($"NZ: {av.Name} R{raceNum} " +
|
||||
$"[{totalMeetings} meeting]");
|
||||
|
||||
try
|
||||
{
|
||||
var resp = _client.TryGetRaceForm(date, av.Slug,
|
||||
raceNum, "gallops", "nz", Timezone, ct);
|
||||
|
||||
if (resp == null || string.IsNullOrEmpty(resp.Content))
|
||||
break;
|
||||
|
||||
ParseRaceFormIntoTable(dt, resp.Content, "nz");
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch
|
||||
{
|
||||
totalErrors++;
|
||||
break;
|
||||
}
|
||||
|
||||
completedNzRaces++;
|
||||
int pct = downloadBase + (int)((double)completedNzRaces / Math.Max(estimatedNzRaces, 1) * downloadSpan);
|
||||
progress?.Report(Math.Min(pct, phaseBase + phaseSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status?.Report("NZ: Nessun meeting attivo trovato");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status?.Report("NZ: Nessuna pista trovata");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
totalErrors++;
|
||||
System.Diagnostics.Debug.WriteLine($"Errore fase NZ: {ex.Message}");
|
||||
}
|
||||
|
||||
currentPhase++;
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
string errMsg = errors > 0 ? $" ({errors} errori)" : "";
|
||||
status?.Report($"Trovati {dt.Rows.Count} corridori in {meetings.Count} meeting{errMsg}");
|
||||
string errMsg = totalErrors > 0 ? $" ({totalErrors} errori)" : "";
|
||||
string countries = string.Join("+", requestedCountries.Select(c => c.ToUpper()));
|
||||
status?.Report($"{countries}: {dt.Rows.Count} corridori in {totalMeetings} meeting{errMsg}");
|
||||
return dt;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -124,55 +312,85 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
}
|
||||
}
|
||||
|
||||
#region DataTable creation
|
||||
#region Venues
|
||||
|
||||
private DataTable CreateRunnerTable()
|
||||
private class VenueInfo
|
||||
{
|
||||
var dt = new DataTable();
|
||||
// Campi corsa
|
||||
dt.Columns.Add("Ippodromo", typeof(string));
|
||||
dt.Columns.Add("Corsa N.", typeof(int));
|
||||
dt.Columns.Add("Nome Corsa", typeof(string));
|
||||
dt.Columns.Add("Orario", typeof(string));
|
||||
dt.Columns.Add("Distanza", typeof(string));
|
||||
dt.Columns.Add("Terreno", typeof(string));
|
||||
dt.Columns.Add("Classe", typeof(string));
|
||||
dt.Columns.Add("Meteo", typeof(string));
|
||||
dt.Columns.Add("Premio", typeof(string));
|
||||
dt.Columns.Add("N. Corridori", typeof(int));
|
||||
// Campi corridore
|
||||
dt.Columns.Add("Num", typeof(int));
|
||||
dt.Columns.Add("Cavallo", typeof(string));
|
||||
dt.Columns.Add("Fantino", typeof(string));
|
||||
dt.Columns.Add("Allenatore", typeof(string));
|
||||
dt.Columns.Add("Peso", typeof(string));
|
||||
dt.Columns.Add("Claim", typeof(string));
|
||||
dt.Columns.Add("Box", typeof(string));
|
||||
dt.Columns.Add("Età", typeof(string));
|
||||
dt.Columns.Add("Forma", typeof(string));
|
||||
dt.Columns.Add("Ultimi 20", typeof(string));
|
||||
dt.Columns.Add("Colori", typeof(string));
|
||||
dt.Columns.Add("Cambio Equip.", typeof(string));
|
||||
// Statistiche overall
|
||||
dt.Columns.Add("Vitt.", typeof(string));
|
||||
dt.Columns.Add("Piazz.", typeof(string));
|
||||
dt.Columns.Add("Partenze", typeof(string));
|
||||
dt.Columns.Add("% Vitt.", typeof(string));
|
||||
dt.Columns.Add("% Piazz.", typeof(string));
|
||||
// Statistiche pista
|
||||
dt.Columns.Add("Pista V/P/S", typeof(string));
|
||||
// Statistiche distanza
|
||||
dt.Columns.Add("Dist. V/P/S", typeof(string));
|
||||
// Statistiche condizione
|
||||
dt.Columns.Add("Cond. V/P/S", typeof(string));
|
||||
// Stato
|
||||
dt.Columns.Add("Ritirato", typeof(string));
|
||||
return dt;
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Country { get; set; }
|
||||
}
|
||||
|
||||
private class ActiveVenue
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Country { get; set; }
|
||||
public string FirstRaceContent { get; set; }
|
||||
}
|
||||
|
||||
private List<VenueInfo> GetFilteredVenues(string country, CancellationToken ct)
|
||||
{
|
||||
if (_venuesCache == null)
|
||||
_venuesCache = ParseVenues(_client.GetVenues(ct).Content);
|
||||
|
||||
return _venuesCache
|
||||
.Where(v => string.Equals(v.Country, country, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.IsNullOrEmpty(v.Name))
|
||||
.OrderBy(v => v.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static List<VenueInfo> ParseVenues(string json)
|
||||
{
|
||||
var venues = new List<VenueInfo>();
|
||||
if (string.IsNullOrEmpty(json)) return venues;
|
||||
|
||||
try
|
||||
{
|
||||
using (var doc = JsonDocument.Parse(json))
|
||||
{
|
||||
var root = doc.RootElement;
|
||||
|
||||
JsonElement arr;
|
||||
if (root.TryGetProperty("venues", out var venuesEl) &&
|
||||
venuesEl.ValueKind == JsonValueKind.Array)
|
||||
arr = venuesEl;
|
||||
else if (root.ValueKind == JsonValueKind.Array)
|
||||
arr = root;
|
||||
else
|
||||
return venues;
|
||||
|
||||
foreach (var v in arr.EnumerateArray())
|
||||
{
|
||||
try
|
||||
{
|
||||
string name = GetString(v, "name", "");
|
||||
string country = GetString(v, "country", "");
|
||||
string raceType = GetString(v, "raceType", "gallops");
|
||||
|
||||
if (raceType != "gallops") continue;
|
||||
if (string.IsNullOrEmpty(name)) continue;
|
||||
|
||||
venues.Add(new VenueInfo
|
||||
{
|
||||
Name = name,
|
||||
Slug = name.ToLowerInvariant().Replace(" ", "-"),
|
||||
Country = country
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return venues;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region JSON Parsing
|
||||
#region Meetings parsing (AU)
|
||||
|
||||
private class MeetingInfo
|
||||
{
|
||||
@@ -242,7 +460,53 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
return meetings;
|
||||
}
|
||||
|
||||
private void ParseRaceFormIntoTable(DataTable dt, string json)
|
||||
#endregion
|
||||
|
||||
#region DataTable creation
|
||||
|
||||
private DataTable CreateRunnerTable()
|
||||
{
|
||||
var dt = new DataTable();
|
||||
dt.Columns.Add("Ippodromo", typeof(string));
|
||||
dt.Columns.Add("Paese", typeof(string));
|
||||
dt.Columns.Add("Corsa N.", typeof(int));
|
||||
dt.Columns.Add("Nome Corsa", typeof(string));
|
||||
dt.Columns.Add("Orario", typeof(string));
|
||||
dt.Columns.Add("Distanza", typeof(string));
|
||||
dt.Columns.Add("Terreno", typeof(string));
|
||||
dt.Columns.Add("Classe", typeof(string));
|
||||
dt.Columns.Add("Meteo", typeof(string));
|
||||
dt.Columns.Add("Premio", typeof(string));
|
||||
dt.Columns.Add("N. Corridori", typeof(int));
|
||||
dt.Columns.Add("Num", typeof(int));
|
||||
dt.Columns.Add("Cavallo", typeof(string));
|
||||
dt.Columns.Add("Fantino", typeof(string));
|
||||
dt.Columns.Add("Allenatore", typeof(string));
|
||||
dt.Columns.Add("Peso", typeof(string));
|
||||
dt.Columns.Add("Claim", typeof(string));
|
||||
dt.Columns.Add("Box", typeof(string));
|
||||
dt.Columns.Add("Eta'", typeof(string));
|
||||
dt.Columns.Add("Forma", typeof(string));
|
||||
dt.Columns.Add("Ultimi 20", typeof(string));
|
||||
dt.Columns.Add("Colori", typeof(string));
|
||||
dt.Columns.Add("Cambio Equip.", typeof(string));
|
||||
dt.Columns.Add("Vitt.", typeof(string));
|
||||
dt.Columns.Add("Piazz.", typeof(string));
|
||||
dt.Columns.Add("Partenze", typeof(string));
|
||||
dt.Columns.Add("% Vitt.", typeof(string));
|
||||
dt.Columns.Add("% Piazz.", typeof(string));
|
||||
dt.Columns.Add("Pista V/P/S", typeof(string));
|
||||
dt.Columns.Add("Dist. V/P/S", typeof(string));
|
||||
dt.Columns.Add("Cond. V/P/S", typeof(string));
|
||||
dt.Columns.Add("Ritirato", typeof(string));
|
||||
return dt;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region JSON Parsing
|
||||
|
||||
private void ParseRaceFormIntoTable(DataTable dt, string json, string fallbackCountry)
|
||||
{
|
||||
if (string.IsNullOrEmpty(json)) return;
|
||||
|
||||
@@ -253,6 +517,7 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
var root = doc.RootElement;
|
||||
|
||||
string track = GetString(root, "track", "");
|
||||
string country = GetString(root, "country", fallbackCountry);
|
||||
int raceNumber = GetInt(root, "raceNumber");
|
||||
string raceName = GetString(root, "raceName", "");
|
||||
string distance = GetString(root, "distance", "");
|
||||
@@ -263,21 +528,11 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
string startTime = GetString(root, "startTime", "");
|
||||
int numberOfRunners = GetInt(root, "numberOfRunners");
|
||||
|
||||
// Formatta orario se è un ISO datetime
|
||||
string orario = "";
|
||||
if (!string.IsNullOrEmpty(startTime))
|
||||
{
|
||||
try
|
||||
{
|
||||
var dto = DateTimeOffset.Parse(startTime);
|
||||
var romeTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
|
||||
orario = TimeZoneInfo.ConvertTime(dto, romeTz).ToString("HH:mm");
|
||||
}
|
||||
catch
|
||||
{
|
||||
orario = startTime;
|
||||
}
|
||||
}
|
||||
string orario = FormatStartTime(startTime);
|
||||
|
||||
string countryDisplay = country.ToUpperInvariant();
|
||||
if (CountryNames.TryGetValue(country.ToLowerInvariant(), out var cn))
|
||||
countryDisplay = cn;
|
||||
|
||||
if (!root.TryGetProperty("runners", out var runnersEl) ||
|
||||
runnersEl.ValueKind != JsonValueKind.Array)
|
||||
@@ -289,8 +544,8 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
{
|
||||
var row = dt.NewRow();
|
||||
|
||||
// Campi corsa
|
||||
row["Ippodromo"] = track;
|
||||
row["Paese"] = countryDisplay;
|
||||
row["Corsa N."] = raceNumber;
|
||||
row["Nome Corsa"] = raceName;
|
||||
row["Orario"] = orario;
|
||||
@@ -301,7 +556,6 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
row["Premio"] = prizeMoney;
|
||||
row["N. Corridori"] = numberOfRunners;
|
||||
|
||||
// Campi corridore
|
||||
row["Num"] = GetInt(runner, "number");
|
||||
row["Cavallo"] = GetString(runner, "name", "");
|
||||
row["Fantino"] = GetString(runner, "jockey", "");
|
||||
@@ -315,7 +569,7 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
row["Box"] = GetInt(runner, "barrier") > 0
|
||||
? GetInt(runner, "barrier").ToString()
|
||||
: GetString(runner, "barrier", "");
|
||||
row["Età"] = GetInt(runner, "age") > 0
|
||||
row["Eta'"] = GetInt(runner, "age") > 0
|
||||
? GetInt(runner, "age").ToString()
|
||||
: GetString(runner, "age", "");
|
||||
row["Forma"] = GetString(runner, "form", "");
|
||||
@@ -323,21 +577,20 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
row["Colori"] = GetString(runner, "racingColours", "");
|
||||
row["Cambio Equip."] = GetString(runner, "gearChange", "");
|
||||
|
||||
// Statistiche overall
|
||||
if (runner.TryGetProperty("stats", out var statsEl))
|
||||
{
|
||||
ParseStatGroup(statsEl, "overall", row, "Vitt.", "Piazz.", "Partenze", "% Vitt.", "% Piazz.");
|
||||
|
||||
ParseStatGroup(statsEl, "overall", row,
|
||||
"Vitt.", "Piazz.", "Partenze", "% Vitt.", "% Piazz.");
|
||||
row["Pista V/P/S"] = FormatStatSummary(statsEl, "track");
|
||||
row["Dist. V/P/S"] = FormatStatSummary(statsEl, "distance");
|
||||
row["Cond. V/P/S"] = FormatStatSummary(statsEl, "condition");
|
||||
}
|
||||
|
||||
// Ritirato
|
||||
bool scratched = false;
|
||||
if (runner.TryGetProperty("scratched", out var scEl) && scEl.ValueKind == JsonValueKind.True)
|
||||
if (runner.TryGetProperty("scratched", out var scEl) &&
|
||||
scEl.ValueKind == JsonValueKind.True)
|
||||
scratched = true;
|
||||
row["Ritirato"] = scratched ? "Sì" : "";
|
||||
row["Ritirato"] = scratched ? "Si" : "";
|
||||
|
||||
dt.Rows.Add(row);
|
||||
}
|
||||
@@ -348,9 +601,30 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estrae wins, places, starts, winPercent, placePercent da un sotto-oggetto stats
|
||||
/// </summary>
|
||||
private static string FormatStartTime(string startTime)
|
||||
{
|
||||
if (string.IsNullOrEmpty(startTime)) return "";
|
||||
|
||||
try
|
||||
{
|
||||
var dto = DateTimeOffset.Parse(startTime);
|
||||
// Converti al fuso orario locale di Roma
|
||||
try
|
||||
{
|
||||
var romeTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
|
||||
return TimeZoneInfo.ConvertTime(dto, romeTz).ToString("HH:mm");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return dto.ToLocalTime().ToString("HH:mm");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseStatGroup(JsonElement statsEl, string group,
|
||||
DataRow row, string winsCol, string placesCol, string startsCol,
|
||||
string winPctCol, string placePctCol)
|
||||
@@ -370,9 +644,6 @@ namespace HorseRacingPredictor.HorseRacing
|
||||
row[placePctCol] = (placePct * 100).ToString("F0") + "%";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formatta un riassunto "V-P/S" per un sotto-gruppo statistico (es. track, distance, condition)
|
||||
/// </summary>
|
||||
private static string FormatStatSummary(JsonElement statsEl, string group)
|
||||
{
|
||||
if (!statsEl.TryGetProperty(group, out var g)) return "";
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace HorseRacingPredictor
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides centralised access to application configuration loaded from appsettings.json.
|
||||
/// Connection strings and API keys that were previously hard-coded are now read from here.
|
||||
/// User-editable preferences (export paths, date formats, …) remain in settings.ini.
|
||||
/// </summary>
|
||||
internal static class AppConfig
|
||||
{
|
||||
private static IConfiguration _configuration;
|
||||
|
||||
public static IConfiguration Configuration => _configuration ??= BuildConfiguration();
|
||||
|
||||
private static IConfiguration BuildConfiguration()
|
||||
{
|
||||
var basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
return new ConfigurationBuilder()
|
||||
.SetBasePath(basePath)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile(
|
||||
$"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json",
|
||||
optional: true,
|
||||
reloadOnChange: true)
|
||||
.Build();
|
||||
}
|
||||
|
||||
// ?? Connection strings ??????????????????????????????????
|
||||
public static string FootballConnectionString =>
|
||||
Configuration.GetConnectionString("Football");
|
||||
|
||||
public static string HorsesConnectionString =>
|
||||
Configuration.GetConnectionString("Horses");
|
||||
|
||||
// ?? API settings ????????????????????????????????????????
|
||||
public static string FootballApiKey =>
|
||||
Configuration["Api:FootballApiKey"] ?? string.Empty;
|
||||
|
||||
public static string FootballApiKeyHeader =>
|
||||
Configuration["Api:FootballApiKeyHeader"] ?? "x-rapidapi-key";
|
||||
|
||||
public static string FootballApiHost =>
|
||||
Configuration["Api:FootballApiHost"] ?? "v3.football.api-sports.io";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HorseRacingPredictor
|
||||
{
|
||||
/// <summary>
|
||||
/// User-editable preferences persisted as JSON.
|
||||
/// Replaces the legacy settings.ini key=value format.
|
||||
/// </summary>
|
||||
internal sealed class UserSettings
|
||||
{
|
||||
private static readonly string FilePath =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "usersettings.json");
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
// ?? Football ????????????????????????????????????????????
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string FbExportPath { get; set; } = string.Empty;
|
||||
public string FbPrefix { get; set; } = string.Empty;
|
||||
public string FbSuffix { get; set; } = string.Empty;
|
||||
public bool FbIncludeDate { get; set; } = true;
|
||||
public string FbDateFormat { get; set; } = "yyyy-MM-dd";
|
||||
public string FbFormat { get; set; } = "CSV";
|
||||
|
||||
// ?? Racing ??????????????????????????????????????????????
|
||||
public string RacingApiKey { get; set; } = string.Empty;
|
||||
public string RcExportPath { get; set; } = string.Empty;
|
||||
public string RcPrefix { get; set; } = string.Empty;
|
||||
public string RcSuffix { get; set; } = string.Empty;
|
||||
public bool RcIncludeDate { get; set; } = true;
|
||||
public string RcDateFormat { get; set; } = "yyyy-MM-dd";
|
||||
public string RcFormat { get; set; } = "CSV";
|
||||
public string RcTimezone { get; set; } = "Australia/Sydney";
|
||||
public List<string> RcCountries { get; set; } = new() { "au", "nz" };
|
||||
|
||||
// ?? Persistence ?????????????????????????????????????????
|
||||
|
||||
public static UserSettings Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(FilePath))
|
||||
return MigrateFromIniOrDefault();
|
||||
|
||||
var json = File.ReadAllText(FilePath);
|
||||
return JsonSerializer.Deserialize<UserSettings>(json, JsonOptions) ?? new UserSettings();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new UserSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var json = JsonSerializer.Serialize(this, JsonOptions);
|
||||
File.WriteAllText(FilePath, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One-time migration: reads old settings.ini if present, converts to UserSettings,
|
||||
/// saves the new usersettings.json, then deletes the ini file.
|
||||
/// </summary>
|
||||
private static UserSettings MigrateFromIniOrDefault()
|
||||
{
|
||||
var iniPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini");
|
||||
if (!File.Exists(iniPath))
|
||||
return new UserSettings();
|
||||
|
||||
var settings = new UserSettings();
|
||||
try
|
||||
{
|
||||
foreach (var line in File.ReadAllLines(iniPath))
|
||||
{
|
||||
var idx = line.IndexOf('=');
|
||||
if (idx < 0) continue;
|
||||
var key = line[..idx].Trim();
|
||||
var val = line[(idx + 1)..].Trim();
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case "ApiKey": settings.ApiKey = val; break;
|
||||
case "FbExportPath": settings.FbExportPath = val; break;
|
||||
case "FbPrefix": settings.FbPrefix = val; break;
|
||||
case "FbSuffix": settings.FbSuffix = val; break;
|
||||
case "FbIncludeDate": settings.FbIncludeDate = val is "1" or "true" or "True"; break;
|
||||
case "FbDateFormat": settings.FbDateFormat = val; break;
|
||||
case "FbFormat": settings.FbFormat = val; break;
|
||||
case "RcExportPath": settings.RcExportPath = val; break;
|
||||
case "RcPrefix": settings.RcPrefix = val; break;
|
||||
case "RcSuffix": settings.RcSuffix = val; break;
|
||||
case "RcIncludeDate": settings.RcIncludeDate = val is "1" or "true" or "True"; break;
|
||||
case "RcDateFormat": settings.RcDateFormat = val; break;
|
||||
case "RcFormat": settings.RcFormat = val; break;
|
||||
case "RacingApiKey": settings.RacingApiKey = val; break;
|
||||
case "RcTimezone": settings.RcTimezone = val; break;
|
||||
case "RcCountries":
|
||||
settings.RcCountries = new List<string>(
|
||||
val.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Persist as JSON and remove legacy file
|
||||
settings.Save();
|
||||
File.Delete(iniPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If migration fails, return whatever we parsed
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"Football": "Server=DESKTOP-9O9JHFS;Database=TestBS_Football;User Id=sa;Password=Asti2019;TrustServerCertificate=True",
|
||||
"Horses": "Server=DESKTOP-9O9JHFS;Database=TestBS_Horses;User Id=sa;Password=Asti2019;TrustServerCertificate=True"
|
||||
},
|
||||
"Api": {
|
||||
"FootballApiKey": "f3795ccef056c5478d316162517d9970",
|
||||
"FootballApiKeyHeader": "x-rapidapi-key",
|
||||
"FootballApiHost": "v3.football.api-sports.io"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using BettingPredictor;
|
||||
@@ -10,16 +10,14 @@ namespace HorseRacingPredictor.Horses
|
||||
{
|
||||
internal class Database : HorseRacingPredictor.Manager.Database
|
||||
{
|
||||
// Implementazione della proprietà astratta _connectionString per il database cavalli
|
||||
protected override string _connectionString => "Server=DESKTOP-9O9JHFS;Database=TestBS_Horses;User Id=sa;Password=Asti2019;";
|
||||
|
||||
private readonly FileReader fileReaderHorses;
|
||||
|
||||
public Database()
|
||||
// Connection string caricata da appsettings.json
|
||||
public Database() : base(AppConfig.HorsesConnectionString)
|
||||
{
|
||||
fileReaderHorses = new FileReader();
|
||||
}
|
||||
|
||||
private readonly FileReader fileReaderHorses;
|
||||
|
||||
public DataTable GetAllHorseRaceData()
|
||||
{
|
||||
DataTable horseRaceData = new DataTable();
|
||||
|
||||
@@ -644,11 +644,6 @@
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<DatePicker x:Name="dpRacing" Width="160"/>
|
||||
<ComboBox x:Name="cmbRaceCode" Width="120" Margin="8,0,0,0" SelectedIndex="0">
|
||||
<ComboBoxItem Content="Galoppo"/>
|
||||
<ComboBoxItem Content="Trotto"/>
|
||||
<ComboBoxItem Content="Levrieri"/>
|
||||
</ComboBox>
|
||||
<Button x:Name="btnDownloadRc" Content="Scarica Corse"
|
||||
Style="{StaticResource AccentBtn}"
|
||||
Background="{StaticResource BrBlue}" Margin="12,0,0,0"
|
||||
@@ -792,6 +787,73 @@
|
||||
<TextBox x:Name="txtRacingApiKey" Style="{StaticResource FlatTb}" MaxWidth="510" HorizontalAlignment="Left"/>
|
||||
|
||||
<Grid Margin="0,14,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="245"/>
|
||||
<ColumnDefinition Width="22"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Text="Timezone (IANA)" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||
<TextBox x:Name="txtRcTimezone" Style="{StaticResource FlatTb}" Text="Australia/Sydney"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="Nazioni" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||
<Grid>
|
||||
<ToggleButton x:Name="btnRcCountriesToggle"
|
||||
Height="34" HorizontalContentAlignment="Left"
|
||||
Padding="10,0,28,0" Cursor="Hand">
|
||||
<ToggleButton.Template>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Grid>
|
||||
<Border x:Name="Bd" CornerRadius="6"
|
||||
Background="{StaticResource BrSurface0}"
|
||||
BorderBrush="{StaticResource BrSurface1}"
|
||||
BorderThickness="1"/>
|
||||
<ContentPresenter Margin="10,0,28,0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"/>
|
||||
<Path Data="M 0,0 L 4,4 L 8,0"
|
||||
Stroke="{StaticResource BrSubtext0}" StrokeThickness="1.5"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="0,0,10,0"/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource BrOverlay0}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource BrBlue}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</ToggleButton.Template>
|
||||
<TextBlock x:Name="lblRcCountriesSummary" Text="AU, NZ"
|
||||
Foreground="{StaticResource BrText}" FontSize="13"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</ToggleButton>
|
||||
<Popup x:Name="popupRcCountries" Placement="Bottom"
|
||||
StaysOpen="False" AllowsTransparency="True"
|
||||
IsOpen="{Binding IsChecked, ElementName=btnRcCountriesToggle, Mode=TwoWay}">
|
||||
<Border Background="{StaticResource BrSurface1}"
|
||||
BorderBrush="{StaticResource BrSurface2}" BorderThickness="1"
|
||||
CornerRadius="8" Padding="6,8" Margin="0,4,0,0"
|
||||
MinWidth="260" MaxHeight="340">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="16" ShadowDepth="4" Color="#40000000"/>
|
||||
</Border.Effect>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel x:Name="pnlRcCountries"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border Height="1" Background="{StaticResource BrBorder}" Margin="0,14,0,14"/>
|
||||
|
||||
<Grid Margin="0,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="245"/>
|
||||
<ColumnDefinition Width="22"/>
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace HorseRacingPredictor
|
||||
InitializeComponent();
|
||||
_footballManager = new Football.Main();
|
||||
_racingManager = new HorseRacing.Main(DefaultRacingApiKey);
|
||||
BuildCountryCheckboxes();
|
||||
// Wire preview update events
|
||||
txtFbPrefix.TextChanged += (s, e) => UpdateFbPreview();
|
||||
txtFbSuffix.TextChanged += (s, e) => UpdateFbPreview();
|
||||
@@ -334,13 +335,111 @@ namespace HorseRacingPredictor
|
||||
|
||||
// ???????????????????? HORSE RACING ????????????????????
|
||||
|
||||
private readonly Dictionary<string, CheckBox> _countryCheckboxes = new Dictionary<string, CheckBox>();
|
||||
|
||||
private void BuildCountryCheckboxes()
|
||||
{
|
||||
if (pnlRcCountries == null) return;
|
||||
pnlRcCountries.Children.Clear();
|
||||
_countryCheckboxes.Clear();
|
||||
|
||||
var supported = new HashSet<string>(HorseRacing.Main.SupportedCountries);
|
||||
|
||||
// Header: nazioni con dati
|
||||
pnlRcCountries.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "Con dati disponibili",
|
||||
FontSize = 10,
|
||||
FontFamily = new System.Windows.Media.FontFamily("Segoe UI Semibold"),
|
||||
Foreground = FindResource("BrBlue") as System.Windows.Media.Brush,
|
||||
Margin = new Thickness(6, 2, 0, 4)
|
||||
});
|
||||
|
||||
foreach (var code in HorseRacing.Main.SupportedCountries)
|
||||
AddCountryCheckbox(code, supported, true);
|
||||
|
||||
// Separator
|
||||
pnlRcCountries.Children.Add(new Border
|
||||
{
|
||||
Height = 1,
|
||||
Background = FindResource("BrBorder") as System.Windows.Media.Brush,
|
||||
Margin = new Thickness(4, 6, 4, 6)
|
||||
});
|
||||
|
||||
// Header: catalogo
|
||||
pnlRcCountries.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "Solo catalogo (nessun dato di forma)",
|
||||
FontSize = 10,
|
||||
Foreground = FindResource("BrOverlay0") as System.Windows.Media.Brush,
|
||||
Margin = new Thickness(6, 2, 0, 4)
|
||||
});
|
||||
|
||||
foreach (var code in HorseRacing.Main.AllCountries)
|
||||
{
|
||||
if (supported.Contains(code)) continue;
|
||||
AddCountryCheckbox(code, supported, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCountryCheckbox(string code, HashSet<string> supported, bool isSupported)
|
||||
{
|
||||
string label = HorseRacing.Main.CountryNames.TryGetValue(code, out var name)
|
||||
? $"{name} ({code.ToUpper()})"
|
||||
: code.ToUpper();
|
||||
|
||||
var cb = new CheckBox
|
||||
{
|
||||
Content = label,
|
||||
Tag = code,
|
||||
IsChecked = false,
|
||||
Margin = new Thickness(4, 2, 4, 2),
|
||||
FontSize = 12,
|
||||
Foreground = isSupported
|
||||
? FindResource("BrText") as System.Windows.Media.Brush
|
||||
: FindResource("BrOverlay0") as System.Windows.Media.Brush,
|
||||
Opacity = isSupported ? 1.0 : 0.7
|
||||
};
|
||||
cb.Checked += (s, e) => UpdateCountriesSummary();
|
||||
cb.Unchecked += (s, e) => UpdateCountriesSummary();
|
||||
|
||||
_countryCheckboxes[code] = cb;
|
||||
pnlRcCountries.Children.Add(cb);
|
||||
}
|
||||
|
||||
private List<string> GetSelectedCountries()
|
||||
{
|
||||
return _countryCheckboxes
|
||||
.Where(kv => kv.Value.IsChecked == true)
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void SetSelectedCountries(IEnumerable<string> codes)
|
||||
{
|
||||
var set = new HashSet<string>(codes.Select(c => c.Trim().ToLowerInvariant()));
|
||||
foreach (var kv in _countryCheckboxes)
|
||||
kv.Value.IsChecked = set.Contains(kv.Key);
|
||||
UpdateCountriesSummary();
|
||||
}
|
||||
|
||||
private void UpdateCountriesSummary()
|
||||
{
|
||||
var selected = GetSelectedCountries();
|
||||
if (lblRcCountriesSummary != null)
|
||||
{
|
||||
lblRcCountriesSummary.Text = selected.Count > 0
|
||||
? string.Join(", ", selected.Select(c => c.ToUpper()))
|
||||
: "Nessuna";
|
||||
}
|
||||
}
|
||||
|
||||
private void rbRcSource_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Toggle visibility of API vs CSV controls
|
||||
if (dpRacing == null || btnDownloadRc == null || btnBrowseCsvRc == null) return;
|
||||
bool isApi = rbRcApi.IsChecked == true;
|
||||
dpRacing.Visibility = isApi ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (cmbRaceCode != null) cmbRaceCode.Visibility = isApi ? Visibility.Visible : Visibility.Collapsed;
|
||||
btnDownloadRc.Visibility = isApi ? Visibility.Visible : Visibility.Collapsed;
|
||||
btnBrowseCsvRc.Visibility = isApi ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
@@ -528,20 +627,9 @@ namespace HorseRacingPredictor
|
||||
r[col] = n++;
|
||||
}
|
||||
|
||||
private string GetSelectedRaceCode()
|
||||
{
|
||||
int idx = cmbRaceCode?.SelectedIndex ?? 0;
|
||||
switch (idx)
|
||||
{
|
||||
case 1: return "harness";
|
||||
case 2: return "greyhounds";
|
||||
default: return "gallops";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadRacecardsAsync()
|
||||
{
|
||||
// Se è già in corso, annulla
|
||||
// Se e' gia' in corso, annulla
|
||||
if (_racingCts != null)
|
||||
{
|
||||
_racingCts.Cancel();
|
||||
@@ -557,20 +645,21 @@ namespace HorseRacingPredictor
|
||||
try
|
||||
{
|
||||
pbRacing.Value = 0;
|
||||
lblStatusRc.Text = "Scaricamento corse da FormFav…";
|
||||
lblStatusRc.Text = "Scaricamento corse da FormFav...";
|
||||
btnDownloadRc.Content = "Annulla";
|
||||
dpRacing.IsEnabled = false;
|
||||
cmbRaceCode.IsEnabled = false;
|
||||
btnExportRcCsv.IsEnabled = false;
|
||||
|
||||
// Applica impostazioni correnti al manager
|
||||
ApplyRacingSettings();
|
||||
|
||||
var progress = new Progress<int>(v => pbRacing.Value = v);
|
||||
var status = new Progress<string>(s => lblStatusRc.Text = s);
|
||||
|
||||
var date = dpRacing.SelectedDate ?? DateTime.Today;
|
||||
string raceCode = GetSelectedRaceCode();
|
||||
|
||||
var table = await Task.Run(() =>
|
||||
_racingManager.GetAllRacesForDate(date, raceCode, progress, status, ct), ct);
|
||||
_racingManager.GetAllRacesForDate(date, progress, status, ct), ct);
|
||||
|
||||
_racingData = table;
|
||||
|
||||
@@ -606,10 +695,22 @@ namespace HorseRacingPredictor
|
||||
_racingCts = null;
|
||||
btnDownloadRc.Content = "Scarica Corse";
|
||||
dpRacing.IsEnabled = true;
|
||||
cmbRaceCode.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRacingSettings()
|
||||
{
|
||||
_racingManager.UpdateApiKey(txtRacingApiKey.Text.Trim());
|
||||
|
||||
var tz = txtRcTimezone?.Text?.Trim();
|
||||
if (!string.IsNullOrEmpty(tz))
|
||||
_racingManager.Timezone = tz;
|
||||
|
||||
var selected = GetSelectedCountries();
|
||||
if (selected.Count > 0)
|
||||
_racingManager.Countries = selected;
|
||||
}
|
||||
|
||||
private void btnExportRcCsv_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var rcDate = dpRacing.SelectedDate ?? DateTime.Today;
|
||||
@@ -730,46 +831,36 @@ namespace HorseRacingPredictor
|
||||
return null;
|
||||
}
|
||||
|
||||
// ???????????????????? SETTINGS ????????????????????
|
||||
|
||||
private string SettingsFilePath =>
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini");
|
||||
// —————————————————— SETTINGS ——————————————————
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
txtRacingApiKey.Text = DefaultRacingApiKey;
|
||||
var s = UserSettings.Load();
|
||||
|
||||
if (!File.Exists(SettingsFilePath)) return;
|
||||
foreach (var line in File.ReadAllLines(SettingsFilePath))
|
||||
{
|
||||
var idx = line.IndexOf('=');
|
||||
if (idx < 0) continue;
|
||||
var key = line.Substring(0, idx).Trim();
|
||||
var val = line.Substring(idx + 1).Trim();
|
||||
txtApiKey.Text = s.ApiKey;
|
||||
txtFbExportPath.Text = s.FbExportPath;
|
||||
txtFbPrefix.Text = s.FbPrefix;
|
||||
txtFbSuffix.Text = s.FbSuffix;
|
||||
chkFbIncludeDate.IsChecked = s.FbIncludeDate;
|
||||
SetComboBoxSelectionByContent(cmbFbDateFormat, s.FbDateFormat);
|
||||
SetComboBoxSelectionByContent(cmbFbFormat, s.FbFormat);
|
||||
|
||||
if (key == "ApiKey") txtApiKey.Text = val;
|
||||
else if (key == "FbExportPath") txtFbExportPath.Text = val;
|
||||
else if (key == "FbPrefix") txtFbPrefix.Text = val;
|
||||
else if (key == "FbSuffix") txtFbSuffix.Text = val;
|
||||
else if (key == "FbIncludeDate") chkFbIncludeDate.IsChecked = val == "1" || val.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
else if (key == "FbDateFormat") { try { SetComboBoxSelectionByContent(cmbFbDateFormat, val); } catch { } }
|
||||
else if (key == "FbFormat") { try { SetComboBoxSelectionByContent(cmbFbFormat, val); } catch { } }
|
||||
else if (key == "RcExportPath") txtRcExportPath.Text = val;
|
||||
else if (key == "RcPrefix") txtRcPrefix.Text = val;
|
||||
else if (key == "RcSuffix") txtRcSuffix.Text = val;
|
||||
else if (key == "RcIncludeDate") chkRcIncludeDate.IsChecked = val == "1" || val.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
else if (key == "RcDateFormat") { try { SetComboBoxSelectionByContent(cmbRcDateFormat, val); } catch { } }
|
||||
else if (key == "RcFormat") { try { SetComboBoxSelectionByContent(cmbRcFormat, val); } catch { } }
|
||||
else if (key == "RacingApiKey") txtRacingApiKey.Text = val;
|
||||
}
|
||||
txtRcExportPath.Text = s.RcExportPath;
|
||||
txtRcPrefix.Text = s.RcPrefix;
|
||||
txtRcSuffix.Text = s.RcSuffix;
|
||||
chkRcIncludeDate.IsChecked = s.RcIncludeDate;
|
||||
SetComboBoxSelectionByContent(cmbRcDateFormat, s.RcDateFormat);
|
||||
SetComboBoxSelectionByContent(cmbRcFormat, s.RcFormat);
|
||||
|
||||
txtRacingApiKey.Text = string.IsNullOrEmpty(s.RacingApiKey) ? DefaultRacingApiKey : s.RacingApiKey;
|
||||
if (txtRcTimezone != null) txtRcTimezone.Text = s.RcTimezone;
|
||||
SetSelectedCountries(s.RcCountries.ToArray());
|
||||
|
||||
// Update preview UI after loading values
|
||||
UpdateFbPreview();
|
||||
UpdateRcPreview();
|
||||
|
||||
_racingManager.UpdateApiKey(txtRacingApiKey.Text);
|
||||
ApplyRacingSettings();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@@ -1000,28 +1091,30 @@ namespace HorseRacingPredictor
|
||||
{
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"ApiKey={txtApiKey.Text.Trim()}");
|
||||
sb.AppendLine($"FbExportPath={txtFbExportPath.Text.Trim()}");
|
||||
sb.AppendLine($"FbPrefix={txtFbPrefix.Text.Trim()}");
|
||||
sb.AppendLine($"FbSuffix={txtFbSuffix.Text.Trim()}");
|
||||
sb.AppendLine($"FbIncludeDate={(chkFbIncludeDate.IsChecked==true?"1":"0")}");
|
||||
sb.AppendLine($"FbDateFormat={(cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd"}");
|
||||
sb.AppendLine($"FbFormat={(cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV"}");
|
||||
sb.AppendLine($"RcExportPath={txtRcExportPath.Text.Trim()}");
|
||||
sb.AppendLine($"RcPrefix={txtRcPrefix.Text.Trim()}");
|
||||
sb.AppendLine($"RcSuffix={txtRcSuffix.Text.Trim()}");
|
||||
sb.AppendLine($"RcIncludeDate={(chkRcIncludeDate.IsChecked==true?"1":"0")}");
|
||||
sb.AppendLine($"RcDateFormat={(cmbRcDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd"}");
|
||||
sb.AppendLine($"RcFormat={(cmbRcFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV"}");
|
||||
sb.AppendLine($"RacingApiKey={txtRacingApiKey.Text.Trim()}");
|
||||
File.WriteAllText(SettingsFilePath, sb.ToString(), Encoding.UTF8);
|
||||
var s = new UserSettings
|
||||
{
|
||||
ApiKey = txtApiKey.Text.Trim(),
|
||||
FbExportPath = txtFbExportPath.Text.Trim(),
|
||||
FbPrefix = txtFbPrefix.Text.Trim(),
|
||||
FbSuffix = txtFbSuffix.Text.Trim(),
|
||||
FbIncludeDate = chkFbIncludeDate.IsChecked == true,
|
||||
FbDateFormat = (cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd",
|
||||
FbFormat = (cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV",
|
||||
RcExportPath = txtRcExportPath.Text.Trim(),
|
||||
RcPrefix = txtRcPrefix.Text.Trim(),
|
||||
RcSuffix = txtRcSuffix.Text.Trim(),
|
||||
RcIncludeDate = chkRcIncludeDate.IsChecked == true,
|
||||
RcDateFormat = (cmbRcDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd",
|
||||
RcFormat = (cmbRcFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV",
|
||||
RacingApiKey = txtRacingApiKey.Text.Trim(),
|
||||
RcTimezone = txtRcTimezone?.Text?.Trim() ?? "Australia/Sydney",
|
||||
RcCountries = new List<string>(GetSelectedCountries())
|
||||
};
|
||||
s.Save();
|
||||
|
||||
// update previews after save
|
||||
UpdateFbPreview();
|
||||
UpdateRcPreview();
|
||||
|
||||
_racingManager.UpdateApiKey(txtRacingApiKey.Text.Trim());
|
||||
ApplyRacingSettings();
|
||||
|
||||
MessageBox.Show("Impostazioni salvate con successo.",
|
||||
"Salvato", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace HorseRacingPredictor.Manager
|
||||
{
|
||||
internal abstract class Database
|
||||
{
|
||||
// La stringa di connessione viene rimossa da qui e definita nelle classi derivate
|
||||
protected abstract string _connectionString { get; }
|
||||
protected readonly string _connectionString;
|
||||
|
||||
protected Database(string connectionString)
|
||||
{
|
||||
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
|
||||
}
|
||||
|
||||
protected SqlConnection GetConnection()
|
||||
{
|
||||
@@ -69,7 +73,7 @@ namespace HorseRacingPredictor.Manager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo per verificare se la connessione al database è valida
|
||||
/// Metodo per verificare se la connessione al database è valida
|
||||
/// </summary>
|
||||
public bool TestConnection()
|
||||
{
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace BettingPredictor.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CsvHelper" version="33.1.0" targetFramework="net481" />
|
||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="Microsoft.Bcl.HashCode" version="6.0.0" targetFramework="net481" />
|
||||
<package id="Microsoft.Bcl.Numerics" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net481" />
|
||||
<package id="Microsoft.ML" version="5.0.0-preview.25503.2" targetFramework="net481" />
|
||||
<package id="Microsoft.ML.CpuMath" version="5.0.0-preview.25503.2" targetFramework="net481" />
|
||||
<package id="Microsoft.ML.DataView" version="5.0.0-preview.25503.2" targetFramework="net481" />
|
||||
<package id="Microsoft.ML.FastTree" version="5.0.0-preview.25503.2" targetFramework="net481" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.3800.47" targetFramework="net481" />
|
||||
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net481" />
|
||||
<package id="RestSharp" version="112.1.1-alpha.0.4" targetFramework="net481" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net481" />
|
||||
<package id="System.CodeDom" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Collections.Immutable" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.IO.Pipelines" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net481" />
|
||||
<package id="System.Numerics.Tensors" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net481" />
|
||||
<package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net481" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net481" />
|
||||
<package id="System.Text.Encodings.Web" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Text.Json" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Threading.Channels" version="10.0.0-rc.1.25451.107" targetFramework="net481" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net481" />
|
||||
<package id="System.ValueTuple" version="4.6.1" targetFramework="net481" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user