Skip to content

Commit

Permalink
docs: Add persistence layer (MSSQL) to WeatherForecast example
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn committed Oct 17, 2022
1 parent 78cf9a3 commit 4dab255
Show file tree
Hide file tree
Showing 16 changed files with 269 additions and 40 deletions.
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Autodetect text files
* text=auto
# Definitively text files
# Definitively text files
*.cs text
*.cake text

# Git lfs
*.crt filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
18 changes: 11 additions & 7 deletions examples/WeatherForecast/Packages.props
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<ItemGroup>
<PackageReference Update="JetBrains.Annotations" Version="2022.1.0" />
<PackageReference Update="Microsoft.Fast.Components.FluentUI" Version="1.5.3" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Update="Selenium.WebDriver.ChromeDriver" Version="106.0.5249.6100" />
<PackageReference Update="Selenium.WebDriver" Version="4.5.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.5" />
<PackageReference Update="xunit" Version="2.4.2" />
<PackageReference Update="JetBrains.Annotations" Version="2022.1.0"/>
<PackageReference Update="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.10"/>
<PackageReference Update="Microsoft.EntityFrameworkCore" Version="6.0.10"/>
<PackageReference Update="Microsoft.Fast.Components.FluentUI" Version="1.5.3"/>
<PackageReference Update="System.ComponentModel.Annotations" Version="5.0.0"/>
<PackageReference Update="System.Text.Json" Version="6.0.6"/>
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
<PackageReference Update="Selenium.WebDriver.ChromeDriver" Version="106.0.5249.6100"/>
<PackageReference Update="Selenium.WebDriver" Version="4.5.1"/>
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.5"/>
<PackageReference Update="xunit" Version="2.4.2"/>
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions examples/WeatherForecast/WeatherForecast.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers", "..\..\src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeatherForecast", "src\WeatherForecast\WeatherForecast.csproj", "{40712E50-0FC0-47AA-A9BE-98AA2FB73E59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeatherForecast.Contexts", "src\WeatherForecast.Contexts\WeatherForecast.Contexts.csproj", "{86938290-5D7D-43B5-8146-4614FB5820F4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeatherForecast.Entities", "src\WeatherForecast.Entities\WeatherForecast.Entities.csproj", "{53CAFB42-4AEB-43F9-B6A1-DAA8EE46F174}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeatherForecast.Interactors", "src\WeatherForecast.Interactors\WeatherForecast.Interactors.csproj", "{6582ADC8-7DE3-4183-95DF-75D63B784023}"
Expand All @@ -31,6 +33,10 @@ Global
{40712E50-0FC0-47AA-A9BE-98AA2FB73E59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40712E50-0FC0-47AA-A9BE-98AA2FB73E59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40712E50-0FC0-47AA-A9BE-98AA2FB73E59}.Release|Any CPU.Build.0 = Release|Any CPU
{86938290-5D7D-43B5-8146-4614FB5820F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86938290-5D7D-43B5-8146-4614FB5820F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86938290-5D7D-43B5-8146-4614FB5820F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86938290-5D7D-43B5-8146-4614FB5820F4}.Release|Any CPU.Build.0 = Release|Any CPU
{53CAFB42-4AEB-43F9-B6A1-DAA8EE46F174}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53CAFB42-4AEB-43F9-B6A1-DAA8EE46F174}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53CAFB42-4AEB-43F9-B6A1-DAA8EE46F174}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using WeatherForecast.Entities;

namespace WeatherForecast.Contexts;

[PublicAPI]
public sealed class WeatherDataContext : DbContext
{
public WeatherDataContext(DbContextOptions<WeatherDataContext> options) : base(options)
{
}

public DbSet<WeatherData> WeatherData { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Map all read-only properties. There is not [Mapped] attribute like [NotMapped].
modelBuilder.Entity<WeatherData>().Property(weatherData => weatherData.Id);
modelBuilder.Entity<WeatherData>().Property(weatherData => weatherData.Date);
modelBuilder.Entity<Temperature>().Property(temperature => temperature.Id);
modelBuilder.Entity<Temperature>().Property(temperature => temperature.UnitName);
modelBuilder.Entity<Temperature>().Property(temperature => temperature.UnitSymbol);
modelBuilder.Entity<Temperature>().Property(temperature => temperature.Value);
modelBuilder.Entity<Temperature>().Property(temperature => temperature.Measured);
modelBuilder.Entity<WeatherData>().HasMany(weatherData => weatherData.Temperatures).WithOne().HasForeignKey(temperature => temperature.BelongsTo);

var weatherDataSeed = Enumerable.Range(0, 30).Select(_ => Guid.NewGuid()).Select((id, day) => new WeatherData(id, DateTime.Today.AddDays(day))).ToList();
var temperatureSeed = weatherDataSeed.SelectMany(data => Enumerable.Range(0, 23).Select(hour => Temperature.Celsius(data.Id, Random.Shared.Next(-10, 30), data.Date.AddHours(hour)))).ToList();

modelBuilder.Entity<Temperature>().HasData(temperatureSeed);
modelBuilder.Entity<WeatherData>().HasData(weatherDataSeed);
base.OnModelCreating(modelBuilder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using WeatherForecast.Entities;
using WeatherForecast.Repositories;

namespace WeatherForecast.Contexts;

[PublicAPI]
public sealed class WeatherDataReadOnlyContext : IWeatherDataReadOnlyRepository
{
private readonly WeatherDataContext _context;

public WeatherDataReadOnlyContext(WeatherDataContext context)
{
_context = context;
_context.Database.EnsureCreated();
}

public Task<IEnumerable<WeatherData>> GetAllAsync()
{
throw new NotImplementedException();
}

public Task<IEnumerable<WeatherData>> GetAllAsync(string latitude, string longitude, DateTime from, DateTime to)
{
return Task.FromResult<IEnumerable<WeatherData>>(_context.WeatherData.Include(property => property.Temperatures).OrderBy(weatherData => weatherData.Date).Take(to.Subtract(from).Days));
}

public Task<WeatherData> GetAsync(Guid id)
{
throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WeatherForecast.Entities;
using WeatherForecast.Repositories;

namespace WeatherForecast.Contexts;

[PublicAPI]
public sealed class WeatherDataWriteOnlyContext : IWeatherDataWriteOnlyRepository
{
public WeatherDataWriteOnlyContext(WeatherDataContext context)
{
_ = context;
}

public Task CreateAsync(WeatherData weatherData)
{
throw new NotImplementedException();
}

public Task UpdateAsync(WeatherData weatherData)
{
throw new NotImplementedException();
}

public Task DeleteAsync(WeatherData weatherData)
{
throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3"/>
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations"/>
<PackageReference Include="Microsoft.EntityFrameworkCore"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/WeatherForecast.Repositories/WeatherForecast.Repositories.csproj"/>
</ItemGroup>
</Project>
20 changes: 20 additions & 0 deletions examples/WeatherForecast/src/WeatherForecast.Entities/HasId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using JetBrains.Annotations;

namespace WeatherForecast.Entities;

[PublicAPI]
public abstract class HasId
{
[JsonConstructor]
public HasId(Guid id)
{
Id = id;
}

[Key]
[JsonPropertyName("id")]
public Guid Id { get; }
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,51 @@
using System;
using System.Text.Json.Serialization;
using JetBrains.Annotations;

namespace WeatherForecast.Entities;

[PublicAPI]
public readonly struct Temperature
public sealed class Temperature : HasId
{
private Temperature(string unitName, string unitSymbol, double value, DateTime measured)
[JsonConstructor]
public Temperature(Guid id, Guid belongsTo, string unitName, string unitSymbol, double value, DateTime measured) : base(id)
{
BelongsTo = belongsTo;
UnitName = unitName;
UnitSymbol = unitSymbol;
Value = value;
Measured = measured;
}

public static Temperature Kelvin(double value, DateTime measured)
{
return new Temperature("Kelvin", "K", value, measured);
}

public static Temperature Celsius(double value, DateTime measured)
{
return new Temperature("degree Celsius", "°C", value, measured);
}
public static Temperature AbsoluteZero { get; } = Temperature.Kelvin(Guid.Empty, 0, DateTime.MinValue);

public static Temperature Fahrenheit(double value, DateTime measured)
{
return new Temperature("degree Fahrenheit", "°F", value, measured);
}
[JsonPropertyName("belongsTo")]
public Guid BelongsTo { get; }

[JsonPropertyName("unitName")]
public string UnitName { get; }

[JsonPropertyName("unitSymbol")]
public string UnitSymbol { get; }

[JsonPropertyName("value")]
public double Value { get; }

[JsonPropertyName("measured")]
public DateTime Measured { get; }

public static Temperature Kelvin(Guid belongsTo, double value, DateTime measured)
{
return new Temperature(Guid.NewGuid(), belongsTo, "Kelvin", "K", value, measured);
}

public static Temperature Celsius(Guid belongsTo, double value, DateTime measured)
{
return new Temperature(Guid.NewGuid(), belongsTo, "degree Celsius", "°C", value, measured);
}

public static Temperature Fahrenheit(Guid belongsTo, double value, DateTime measured)
{
return new Temperature(Guid.NewGuid(), belongsTo, "degree Fahrenheit", "°F", value, measured);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using JetBrains.Annotations;

namespace WeatherForecast.Entities;

[PublicAPI]
public readonly struct WeatherData
public sealed class WeatherData : HasId
{
public WeatherData(DateTime date, IEnumerable<Temperature> measurements)
public WeatherData(Guid id, DateTime date) : this(id, date, new List<Temperature>())
{
// Entity Framework constructor.
}

[JsonConstructor]
public WeatherData(Guid id, DateTime date, IList<Temperature> temperatures) : base(id)
{
IReadOnlyCollection<Temperature> temperatures = measurements.ToList();
Date = date;
Minimum = temperatures.OrderBy(temperature => temperature.Value).FirstOrDefault();
Maximum = temperatures.OrderBy(temperature => temperature.Value).LastOrDefault();
Minimum = temperatures.OrderBy(temperature => temperature.Value).DefaultIfEmpty(Temperature.AbsoluteZero).First();
Maximum = temperatures.OrderBy(temperature => temperature.Value).DefaultIfEmpty(Temperature.AbsoluteZero).Last();
Temperatures = temperatures;
}

[JsonPropertyName("date")]
public DateTime Date { get; }

[JsonIgnore]
public Temperature Minimum { get; }

[JsonIgnore]
public Temperature Maximum { get; }

public IEnumerable<Temperature> Temperatures { get; }
[JsonPropertyName("temperatures")]
public IList<Temperature> Temperatures { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations"/>
<PackageReference Include="System.ComponentModel.Annotations"/>
<PackageReference Include="System.Text.Json"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public Task<IEnumerable<WeatherData>> GetAllAsync()

public Task<IEnumerable<WeatherData>> GetAllAsync(string latitude, string longitude, DateTime from, DateTime to)
{
return Task.FromResult(Enumerable.Range(0, to.Subtract(from).Days).Select(day => DateTime.Today.AddDays(day)).Select(day => new WeatherData(day, Enumerable.Range(0, 12).Select(hour => Temperature.Celsius(Random.Value.Next(-10, 30), DateTime.Today.AddHours(hour))))));
return Task.FromResult(Enumerable.Range(0, to.Subtract(from).Days).Select(_ => Guid.NewGuid()).Select((id, day) => new WeatherData(id, DateTime.Today.AddDays(day), Enumerable.Range(0, 23).Select(hour => Temperature.Celsius(id, Random.Value.Next(-10, 30), DateTime.Today.AddDays(day).AddHours(hour))).ToList())));
}

public Task<WeatherData> GetAsync(Guid id)
Expand Down
22 changes: 19 additions & 3 deletions examples/WeatherForecast/src/WeatherForecast/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Fast.Components.FluentUI;
using WeatherForecast.Contexts;
using WeatherForecast.Interactors.SearchCityOrZipCode;
using WeatherForecast.Repositories;

Expand All @@ -9,9 +12,22 @@
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpClient();
builder.Services.AddFluentUIComponents();
builder.Services.AddSingleton<IWeatherDataReadOnlyRepository, WeatherDataReadOnlyRepository>();
builder.Services.AddSingleton<IWeatherDataWriteOnlyRepository, WeatherDataWriteOnlyRepository>();
builder.Services.AddSingleton<ISearchCityOrZipCode, SearchCityOrZipCode>();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

if (string.IsNullOrWhiteSpace(connectionString))
{
builder.Services.AddSingleton<IWeatherDataReadOnlyRepository, WeatherDataReadOnlyRepository>();
builder.Services.AddSingleton<IWeatherDataWriteOnlyRepository, WeatherDataWriteOnlyRepository>();
builder.Services.AddSingleton<ISearchCityOrZipCode, SearchCityOrZipCode>();
}
else
{
builder.Services.AddDbContext<WeatherDataContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddScoped<IWeatherDataReadOnlyRepository, WeatherDataReadOnlyContext>();
builder.Services.AddScoped<IWeatherDataWriteOnlyRepository, WeatherDataWriteOnlyContext>();
builder.Services.AddScoped<ISearchCityOrZipCode, SearchCityOrZipCode>();
}

var app = builder.Build();
app.UseExceptionHandler("/Error");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"/>
<PackageReference Include="Microsoft.Fast.Components.FluentUI"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/WeatherForecast.Contexts/WeatherForecast.Contexts.csproj"/>
<ProjectReference Include="$(SolutionDir)src/WeatherForecast.Interactors/WeatherForecast.Interactors.csproj"/>
<ProjectReference Include="$(SolutionDir)src/WeatherForecast.Repositories/WeatherForecast.Repositories.csproj"/>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageReference Include="xunit"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../../src/Testcontainers/Testcontainers.csproj" />
<ProjectReference Include="../../../../src/Testcontainers/Testcontainers.csproj"/>
<ProjectReference Include="$(SolutionDir)src/WeatherForecast.Entities/WeatherForecast.Entities.csproj"/>
</ItemGroup>
</Project>
Loading

0 comments on commit 4dab255

Please sign in to comment.