Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions DevHome.sln
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RepositoryManagement", "Rep
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHome.RepositoryManagement", "tools\RepositoryManagement\DevHome.RepositoryManagement\DevHome.RepositoryManagement.csproj", "{B4B2F5EA-BCD3-4F3E-856C-F6C32433BA41}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHome.Database", "database\DevHome.Database\DevHome.Database.csproj", "{CC4E406E-920A-408C-8CF8-098F680B1E29}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug_FailFast|arm64 = Debug_FailFast|arm64
Expand Down Expand Up @@ -1183,6 +1185,24 @@ Global
{B4B2F5EA-BCD3-4F3E-856C-F6C32433BA41}.Release|x64.Build.0 = Release|x64
{B4B2F5EA-BCD3-4F3E-856C-F6C32433BA41}.Release|x86.ActiveCfg = Release|x86
{B4B2F5EA-BCD3-4F3E-856C-F6C32433BA41}.Release|x86.Build.0 = Release|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|arm64.ActiveCfg = Debug|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|arm64.Build.0 = Debug|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|x64.ActiveCfg = Debug|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|x64.Build.0 = Debug|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|x86.ActiveCfg = Debug|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug_FailFast|x86.Build.0 = Debug|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|arm64.ActiveCfg = Debug|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|arm64.Build.0 = Debug|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|x64.ActiveCfg = Debug|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|x64.Build.0 = Debug|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|x86.ActiveCfg = Debug|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Debug|x86.Build.0 = Debug|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|arm64.ActiveCfg = Release|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|arm64.Build.0 = Release|ARM64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|x64.ActiveCfg = Release|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|x64.Build.0 = Release|x64
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|x86.ActiveCfg = Release|x86
{CC4E406E-920A-408C-8CF8-098F680B1E29}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.EntityFrameworkCore;

namespace DevHome.Database.DatabaseModels.RepositoryManagement;

/// <summary>
/// This represents the SQLite model EF has in the database.
/// Any change here needs a corresponding migration.
/// Use FluentAPI in the database context to further customize each column.
/// </summary>
[Index(nameof(RepositoryName), nameof(RepositoryClonePath), IsUnique = true)]
public class Repository
{
public int RepositoryId { get; set; }

public string? RepositoryName { get; set; }

public string? RepositoryClonePath { get; set; }

public DateTime? CreatedUTCDate { get; set; }

public DateTime? UpdatedUTCDate { get; set; }

// 1:1 relationship. Repository is the parent and needs only
// the object of the dependant.
public RepositoryMetadata? RepositoryMetadata { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace DevHome.Database.DatabaseModels.RepositoryManagement;

/// <summary>
/// This represents the SQLite model EF has in the database.
/// Any change here needs a corresponding migration.
/// Use FluentAPI in the database context to further customize each column.
/// </summary>
public class RepositoryMetadata
{
// EF uses [ClassName]Id as the primary key
public int RepositoryMetadataId { get; set; }

public bool IsHiddenFromPage { get; set; }

public DateTime UtcDateHidden { get; set; }

public DateTime? CreatedUTCDate { get; set; }

public DateTime? UpdatedUTCDate { get; set; }

public int RepositoryId { get; set; }

public Repository Repository { get; set; } = null!;
}
18 changes: 18 additions & 0 deletions database/DevHome.Database/DevHome.Database.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>

</Project>
54 changes: 54 additions & 0 deletions database/DevHome.Database/DevHomeDatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.ComponentModel.DataAnnotations.Schema;
using DevHome.Database.DatabaseModels.RepositoryManagement;
using Microsoft.EntityFrameworkCore;

namespace DevHome.Database;

public class DevHomeDatabaseContext : DbContext
{
public DbSet<Repository> Repositories { get; set; }

public DbSet<RepositoryMetadata> RepositoryMetadatas { get; set; }

public string DbPath { get; }

public DevHomeDatabaseContext()
{
// Not the final path. It will change before going into main.
// Needs a configurable location for testing.
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = Path.Join(path, "DevHome.db");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// If I have time, this should be split like service extensions.
// As more entities are added, the longer this method will get.
var repositoryEntity = modelBuilder.Entity<Repository>();
if (repositoryEntity != null)
{
repositoryEntity.Property(x => x.RepositoryClonePath).HasDefaultValue(string.Empty).IsRequired(true);
repositoryEntity.Property(x => x.RepositoryName).HasDefaultValue(string.Empty).IsRequired(true);
repositoryEntity.Property(x => x.CreatedUTCDate).HasDefaultValueSql("datetime()");
repositoryEntity.Property(x => x.UpdatedUTCDate).HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc));
repositoryEntity.ToTable("Repository");
}

var repositoryMetadataEntity = modelBuilder.Entity<RepositoryMetadata>();
if (repositoryMetadataEntity != null)
{
repositoryMetadataEntity.Property(x => x.IsHiddenFromPage).HasDefaultValue(false).IsRequired(true);
repositoryMetadataEntity.Property(x => x.UtcDateHidden).HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc)).IsRequired(true);
repositoryMetadataEntity.Property(x => x.CreatedUTCDate).HasDefaultValueSql("datetime()");
repositoryMetadataEntity.Property(x => x.UpdatedUTCDate).HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc));
repositoryMetadataEntity.ToTable("RepositoryMetadata");
}
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite($"Data Source={DbPath}");
}
17 changes: 17 additions & 0 deletions database/DevHome.Database/Extensions/ServiceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DevHome.Database.Extensions;

public static class ServiceExtensions
{
public static IServiceCollection AddDatabaseContext(this IServiceCollection services, HostBuilderContext context)
{
services.AddTransient<DevHomeDatabaseContext>();

return services;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace DevHome.Database.Migrations;

/// <inheritdoc />
public partial class InitialMigration : Migration
{
private static readonly string[] _columns = new[] { "RepositoryName", "RepositoryClonePath" };

/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Repository",
columns: table => new
{
RepositoryId = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RepositoryName = table.Column<string>(type: "TEXT", nullable: false, defaultValue: string.Empty),
RepositoryClonePath = table.Column<string>(type: "TEXT", nullable: false, defaultValue: string.Empty),
CreatedUTCDate = table.Column<DateTime>(type: "TEXT", nullable: true, defaultValueSql: "datetime()"),
UpdatedUTCDate = table.Column<DateTime>(type: "TEXT", nullable: true, defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)),
},
constraints: table =>
{
table.PrimaryKey("PK_Repository", x => x.RepositoryId);
});

migrationBuilder.CreateTable(
name: "RepositoryMetadata",
columns: table => new
{
RepositoryMetadataId = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
IsHiddenFromPage = table.Column<bool>(type: "INTEGER", nullable: false, defaultValue: false),
UtcDateHidden = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)),
CreatedUTCDate = table.Column<DateTime>(type: "TEXT", nullable: true, defaultValueSql: "datetime()"),
UpdatedUTCDate = table.Column<DateTime>(type: "TEXT", nullable: true, defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)),
RepositoryId = table.Column<int>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_RepositoryMetadata", x => x.RepositoryMetadataId);
table.ForeignKey(
name: "FK_RepositoryMetadata_Repository_RepositoryId",
column: x => x.RepositoryId,
principalTable: "Repository",
principalColumn: "RepositoryId",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex(
name: "IX_Repository_RepositoryName_RepositoryClonePath",
table: "Repository",
columns: _columns,
unique: true);

migrationBuilder.CreateIndex(
name: "IX_RepositoryMetadata_RepositoryId",
table: "RepositoryMetadata",
column: "RepositoryId",
unique: true);
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "RepositoryMetadata");

migrationBuilder.DropTable(
name: "Repository");
}
}
Loading