18 Commits

Author SHA1 Message Date
Developer 02
7d2098092a Refactor user DTOs and update faker method
- Aktualisiert `CreateUserDtoFaker` um `UserCreateDto` anstelle von `UserDto` zu generieren.
- Die Klasse `UserDto` wurde entfernt, da sie nicht mehr benötigt wird.
- Hinzufügen von `UserCreateDto` für die Erstellung von Benutzern, Erweiterung von `UserBase`.
- Einführung von `UserReadDto` zum Lesen von Benutzerdaten, ebenfalls eine Erweiterung von `UserBase`.
2025-04-17 17:39:48 +02:00
Developer 02
e3b9d2971b Refactor User and UserDto to inherit from UserBase
- Aktualisierte `User` und `UserDto` Klassen um von einer neuen `UserBase` Klasse zu erben.
- Verschieben der Eigenschaften `Vorname`, `Email` und `Alter` zu `UserBase`.
- Implementierung der überschriebenen Methoden `GetHashCode` und `Equals` sowohl in `User` als auch in `UserDto`, um die Eigenschaften der Basisklasse zu nutzen.
2025-04-17 17:36:07 +02:00
Developer 02
3a604ede88 Refactor user creation and retrieval in tests
Updated `DbRepositoryTests` to await `CreateAsync` directly for user creation. Changed assertion to compare the created user with the user retrieved from the repository, enhancing test accuracy.
2025-04-17 16:43:15 +02:00
Developer 02
476c86ff0a feat: Verbesserung von IRepository mit neuen asynchronen Methoden und Erweiterungen
Aktualisiert IRepository<TEntity> um UpdateAsync<TDto>, DeleteAsync<TDto> und eine neue Überladung von ReadAsync. Die frühere ReadAsync-Überladung für Core.Tests.Mock.User wurde entfernt.

Einführung einer neuen Extensions-Klasse mit Methoden für ReadFirstOrDefaultAsync, ReadFirstAsync, ReadSingleOrDefaultAsync und ReadSingleAsync unter Nutzung der aktualisierten ReadAsync-Methode.
2025-04-17 16:24:52 +02:00
Developer 02
9376fcff86 Enhance IRepository and update DbRepositoryTests
- Added ReadAsync method to IRepository for user retrieval.
- Introduced _userRepo field in DbRepositoryTests for better clarity.
- Modified Setup method to initialize _userRepo from the service provider.
- Updated CreateAsync_ShouldNotThrow to use _userRepo.
- Added ReadAsync_ShouldReturnCreated test for user retrieval validation.
2025-04-17 16:00:59 +02:00
Developer 02
06df97597e Refactor user creation tests in DbRepositoryTests
Die Testmethode `CreateAsync_ShouldPersistUser` wurde entfernt und `CreateAsync_ShouldNotThrow` mit Testfällen für die Erstellung einzelner und mehrerer Benutzer hinzugefügt. Die neue Methode prüft, dass `CreateAsync` keine Ausnahmen auslöst, während Benutzerdaten mit einem Faker erzeugt werden.
2025-04-17 15:48:06 +02:00
Developer 02
266d03e0a1 Add user creation test to DbRepositoryTests
This commit introduces a new test method `CreateAsync_ShouldPersistUser` in `DbRepositoryTests.cs`. The method tests the asynchronous creation of a user in the repository using the `Faker` library to generate user data. It asserts that no exceptions are thrown during the creation process. Additionally, the using directives have been updated to include `DigitalData.Core.Abstractions.Infrastructure`.
2025-04-17 15:31:37 +02:00
Developer 02
e752c6f6ab Add AutoMapper setup to DbRepositoryTests
This commit introduces a using directive for `System.Reflection` in `DbRepositoryTests.cs` to support reflection features. The `Setup` method is updated to include `AddAutoMapper`, ensuring AutoMapper services are registered for dependency injection in the test environment.
2025-04-17 15:17:57 +02:00
Developer 02
561a751de4 Update dependency injection and repository tests
Modified `AddDbRepository` method to allow broader entity types and registered `queryFactory` as a singleton. Added necessary using directives in `DbRepositoryTests.cs` and updated the `Setup` method to configure an in-memory database for testing with `MockDbContext` and `User` entity.
2025-04-17 15:05:23 +02:00
Developer 02
35050d65a8 Refactor Fake class and enhance MockDbContext
Updated the `Fake` class to introduce flexible methods for generating fake `User` and `UserDto` objects with optional parameters. Removed the static `UserFaker` instance and related properties.

Added a new `DbSet<User>` property to `MockDbContext` for improved management of `User` entities.
2025-04-17 14:47:37 +02:00
Developer 02
cf9041980d feat(Mock): Hinzufügen von gefälschten Daten und Benutzermodellen zum Testen
Aktualisiert `DigitalData.Core.Tests.csproj`, um `Bogus` zur Erzeugung gefälschter Daten einzuschließen. Fake.cs„ für die Erstellung von gefälschten “User"-Objekten hinzugefügt. MockDbContext" für das Testen von Datenbank-Interaktionen eingeführt. Definierte `User` und `UserDto` Klassen für die Benutzerdarstellung und Datenübertragung.
2025-04-17 14:34:00 +02:00
Developer 02
cf2ee73ca1 Update target frameworks to include .NET 9.0
Expanded the project's target frameworks to support .NET 9.0, in addition to .NET 7.0 and .NET 8.0.
2025-04-17 13:55:06 +02:00
Developer 02
52f6dc161e Add EF Core package references and DbRepositoryTests
Updated `DigitalData.Core.Tests.csproj` to include conditional
package references for Entity Framework Core and its InMemory
provider for net7.0, net8.0, and net9.0.

Added `DbRepositoryTests` class with setup and teardown
methods for managing the lifecycle of a host instance.
2025-04-17 13:54:25 +02:00
Developer 02
7670898e24 Hinzufügen der Projekte "src" und "tests" zur Projektmappe
Der Projektmappendatei `DigitalData.Core.sln` wurden zwei neue Projekte mit den Namen "src" und "tests" hinzugefügt. Das Projekt „src“ hat einen eindeutigen Bezeichner `{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}`, und das Projekt „tests“ hat den Bezeichner `{EDF84A84-1E01-484E-B073-383F7139C891}`. Darüber hinaus wurden in den Lösungseigenschaften mehrere verschachtelte Projektbeziehungen eingerichtet, die diese neuen Projekte mit den bestehenden Projekten verknüpfen.
2025-04-17 13:32:15 +02:00
Developer 02
f5b202c325 Enhance IRepository and DbRepository with DTO support
Updated IRepository<TEntity> to include new generic methods for creating and reading entities from DTOs. Modified UpdateAsync to accept DTOs instead of generic types. Implemented corresponding methods in DbRepository<TDbContext, TEntity> for mapping DTOs to entities during creation and updating processes.
2025-04-17 13:30:35 +02:00
Developer 02
fffbdf752f Revert "Add mapping methods to IRepository and DbRepository"
This reverts commit 6916e169b1.
2025-04-17 13:15:22 +02:00
Developer 02
6916e169b1 Add mapping methods to IRepository and DbRepository
Updated the `IRepository<TEntity>` interface to include two new mapping methods: `Map<TSource>(TSource source)` and `Map<TDto>(TEntity source)`.

Implemented these methods in the `DbRepository<TDbContext, TEntity>` class, utilizing a `Mapper` for conversions between source types and entity types, as well as between entity types and DTOs.
2025-04-17 13:01:51 +02:00
Developer 02
0c529b199b Bump version numbers and enhance repository interfaces
- Incremented version numbers in project files for updates.
- Marked `ICRUDRepository` as obsolete; use `IRepository` instead.
- Improved parameter names and signatures in `IRepository`.
- Changed access modifiers in `DbRepository` for broader access.
- Updated `CreateAsync` and `UpdateAsync` methods for async operations.
- Implemented `ReadAsync` and `DeleteAsync` methods in `DbRepository`.
- Minor whitespace change in `.csproj` files.
- Retained package description in `DigitalData.Core.Infrastructure.csproj`.
2025-04-17 12:53:40 +02:00
17 changed files with 248 additions and 31 deletions

View File

@@ -17,9 +17,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon>
<Version>3.4.0</Version>
<AssemblyVersion>3.4.0</AssemblyVersion>
<FileVersion>3.4.0</FileVersion>
<Version>3.4.1</Version>
<AssemblyVersion>3.4.1</AssemblyVersion>
<FileVersion>3.4.1</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -0,0 +1,18 @@
using System.Linq.Expressions;
namespace DigitalData.Core.Abstractions.Infrastructure;
public static class Extensions
{
public static async Task<TEntity?> ReadFirstOrDefaultAsync<TEntity>(this IRepository<TEntity> repository, Expression<Func<TEntity, bool>>? expression = null)
=> (await repository.ReadAsync(expression)).FirstOrDefault();
public static async Task<TEntity> ReadFirstAsync<TEntity>(this IRepository<TEntity> repository, Expression<Func<TEntity, bool>>? expression = null)
=> (await repository.ReadAsync(expression)).First();
public static async Task<TEntity?> ReadSingleOrDefaultAsync<TEntity>(this IRepository<TEntity> repository, Expression<Func<TEntity, bool>>? expression = null)
=> (await repository.ReadAsync(expression)).SingleOrDefault();
public static async Task<TEntity> ReadSingleAsync<TEntity>(this IRepository<TEntity> repository, Expression<Func<TEntity, bool>>? expression = null)
=> (await repository.ReadAsync(expression)).Single();
}

View File

@@ -5,7 +5,6 @@
/// </summary>
/// <typeparam name="TEntity">The type of the entity this repository works with.</typeparam>
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
[Obsolete("ICRUDRepository has been deprecated. Please use the IRepository interface instead, which provides a better abtraction (e.g. without tracking) and flexibility.")]
public interface ICRUDRepository<TEntity, TId> where TEntity : class, IUnique<TId>
{
/// <summary>

View File

@@ -4,13 +4,17 @@ namespace DigitalData.Core.Abstractions.Infrastructure;
public interface IRepository<TEntity>
{
public Task<TEntity> CreateAsync(TEntity dto, CancellationToken ct = default);
public Task<TEntity> CreateAsync(TEntity entity, CancellationToken ct = default);
public Task<TEntity> CreateAsync(IEnumerable<TEntity> dtos, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> ReadAsync(Expression? expression = null, CancellationToken ct = default);
public Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> UpdateAsync<TDto>(TDto dto, Expression expression, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> DeleteAsync<TDto>(Expression expression, CancellationToken ct = default);
public Task<IEnumerable<TEntity>> ReadAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default);
public Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken ct = default);
public Task DeleteAsync<TDto>(Expression<Func<TEntity, bool>> expression, CancellationToken ct = default);
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
@@ -14,9 +14,9 @@
<PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackageTags>digital data core application clean architecture</PackageTags>
<Version>3.2.0</Version>
<AssemblyVersion>3.2.0</AssemblyVersion>
<FileVersion>3.2.0</FileVersion>
<Version>3.2.1</Version>
<AssemblyVersion>3.2.1</AssemblyVersion>
<FileVersion>3.2.1</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -5,13 +5,13 @@ using System.Linq.Expressions;
namespace DigitalData.Core.Infrastructure;
internal class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class
public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class
{
protected readonly TDbContext Context;
protected internal readonly TDbContext Context;
protected readonly DbSet<TEntity> Entities;
protected internal readonly DbSet<TEntity> Entities;
protected readonly IMapper Mapper;
protected internal readonly IMapper Mapper;
public DbRepository(TDbContext context, Func<TDbContext, DbSet<TEntity>> queryFactory, IMapper mapper)
{
@@ -20,28 +20,59 @@ internal class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TD
Mapper = mapper;
}
public virtual Task<TEntity> CreateAsync(TEntity entity, CancellationToken ct = default)
public virtual async Task<TEntity> CreateAsync(TEntity entity, CancellationToken ct = default)
{
throw new NotImplementedException();
Entities.Add(entity);
await Context.SaveChangesAsync(ct);
return entity;
}
public virtual Task<TEntity> CreateAsync(IEnumerable<TEntity> entities, CancellationToken ct = default)
public virtual async Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken ct = default)
{
throw new NotImplementedException();
Entities.AddRange(entities);
await Context.SaveChangesAsync(ct);
return entities;
}
public virtual Task<IEnumerable<TEntity>> DeleteAsync<TDto>(Expression expression, CancellationToken ct = default)
public virtual Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken ct = default)
{
throw new NotImplementedException();
var entity = Mapper.Map<TEntity>(dto);
return CreateAsync(entity, ct);
}
public virtual Task<IEnumerable<TEntity>> ReadAsync(Expression? expression = null, CancellationToken ct = default)
public virtual Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken ct = default)
{
throw new NotImplementedException();
var entities = dtos.Select(dto => Mapper.Map<TEntity>(dto));
return CreateAsync(entities, ct);
}
public virtual Task<IEnumerable<TEntity>> UpdateAsync<TDto>(TDto dto, Expression expression, CancellationToken ct = default)
public virtual async Task<IEnumerable<TEntity>> ReadAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default)
=> expression is null
? await Entities.AsNoTracking().ToListAsync(ct)
: await Entities.AsNoTracking().Where(expression).ToListAsync(ct);
public virtual async Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken ct = default)
{
throw new NotImplementedException();
var entities = await Entities.Where(expression).ToListAsync(ct);
foreach (var entity in entities)
{
Mapper.Map(dto, entity);
Entities.Add(entity);
}
await Context.SaveChangesAsync(ct);
}
public virtual async Task DeleteAsync<TDto>(Expression<Func<TEntity, bool>> expression, CancellationToken ct = default)
{
var entities = await Entities.Where(expression).ToListAsync(ct);
foreach (var entity in entities)
{
entities.Remove(entity);
}
await Context.SaveChangesAsync(ct);
}
}

View File

@@ -8,7 +8,7 @@ public static class DependencyInjection
{
public static IServiceCollection AddDbRepository<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>> queryFactory)
where TDbContext : DbContext
where TEntity : DbContext
where TEntity : class
{
return services
.AddScoped<IRepository<TEntity>, DbRepository<TDbContext, TEntity>>()

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
@@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>DigitalData.Core.Infrastructure</PackageId>
<Version>2.0.0.0</Version>
<Version>2.0.1</Version>
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>DigitalData.Core.Infrastructure</Product>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.3" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.20.70" />
@@ -29,4 +30,19 @@
<ProjectReference Include="..\DigitalData.Core.Security\DigitalData.Core.Security.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.20" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.15" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,69 @@
namespace DigitalData.Core.Tests.Infrastructure;
using DigitalData.Core.Infrastructure;
using DigitalData.Core.Tests.Mock;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Reflection;
using DigitalData.Core.Abstractions.Infrastructure;
public class DbRepositoryTests
{
private IHost _host;
private IRepository<User> _userRepo;
[SetUp]
public void Setup()
{
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDbContext<MockDbContext>(opt => opt.UseInMemoryDatabase("MockDB"));
builder.Services.AddDbRepository<MockDbContext, User>(context => context.Users);
builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
_host = builder.Build();
_userRepo = _host.Services.GetRequiredService<IRepository<User>>();
}
[TearDown]
public void TearDown()
{
if (_host is IDisposable disposableHost)
disposableHost.Dispose();
}
[TestCase(true, TestName = "WhenGivenMultipleUsers")]
[TestCase(false, TestName = "WhenGivenSingleUser")]
public void CreateAsync_ShouldNotThrow(bool multiple)
{
// Arrange
var faker = Fake.CreateUserFaker();
// Act & Assert
if (multiple)
Assert.DoesNotThrowAsync(async () => await _userRepo.CreateAsync(faker.Generate(Random.Shared.Next(1, 10))));
else
Assert.DoesNotThrowAsync(async () => await _userRepo.CreateAsync(faker.Generate()));
}
[TestCase(true, TestName = "WhenGivenMultipleUsers")]
[TestCase(false, TestName = "WhenGivenSingleUser")]
public async Task ReadAsync_ShouldReturnCreated(bool multiple)
{
// Arrange
var faker = Fake.CreateUserFaker();
var user = faker.Generate();
// Act
var createdUser = await _userRepo.CreateAsync(user);
var readUser = await _userRepo.ReadFirstOrDefaultAsync(u => u.Id == createdUser.Id);
// Assert
Assert.That(createdUser, Is.EqualTo(readUser));
}
}

View File

@@ -0,0 +1,16 @@
using Bogus;
namespace DigitalData.Core.Tests.Mock;
public static class Fake
{
public static Faker<User> CreateUserFaker(string? firstName = null, string? email = null, int? age = null) => new Faker<User>()
.RuleFor(u => u.FirstName, f => firstName ?? f.Name.FirstName())
.RuleFor(u => u.Email, f => email ?? f.Internet.Email())
.RuleFor(u => u.Age, f => age ?? f.Random.Int(18, 99));
public static Faker<UserCreateDto> CreateUserDtoFaker(string? firstName = null, string? email = null, int? age = null) => new Faker<UserCreateDto>()
.RuleFor(u => u.FirstName, f => firstName ?? f.Name.FirstName())
.RuleFor(u => u.Email, f => email ?? f.Internet.Email())
.RuleFor(u => u.Age, f => age ?? f.Random.Int(18, 99));
}

View File

@@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;
namespace DigitalData.Core.Tests.Mock;
public class MockDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public MockDbContext(DbContextOptions options) : base(options)
{
}
}

View File

@@ -0,0 +1,12 @@
namespace DigitalData.Core.Tests.Mock;
public class User : UserBase
{
public required int Id { get; init; }
public override int GetHashCode() => HashCode.Combine(Id, FirstName, Email, Age);
public override bool Equals(object? obj)
=> (obj is User user && user.GetHashCode() == GetHashCode())
|| (obj is UserBase userBase && userBase.GetHashCode() == base.GetHashCode());
}

View File

@@ -0,0 +1,14 @@
namespace DigitalData.Core.Tests.Mock;
public class UserBase
{
public required string FirstName { get; init; }
public required string Email { get; init; }
public required int Age { get; init; }
public override int GetHashCode() => HashCode.Combine(FirstName, Email, Age);
public override bool Equals(object? obj) => obj is UserBase user && user.GetHashCode() == GetHashCode();
}

View File

@@ -0,0 +1,5 @@
namespace DigitalData.Core.Tests.Mock;
public class UserCreateDto : UserBase
{
}

View File

@@ -0,0 +1,5 @@
namespace DigitalData.Core.Tests.Mock;
public class UserReadDto : UserBase
{
}

View File

@@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security", "Security", "{72
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Abstractions.Security", "DigitalData.Core.Abstractions.Security\DigitalData.Core.Abstractions.Security.csproj", "{C9266749-9504-4EA9-938F-F083357B60B7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EDF84A84-1E01-484E-B073-383F7139C891}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -93,7 +97,19 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A765EBEA-3D1E-4F36-869B-6D72F87FF3F6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{DB404CD9-CBB8-4771-AB1B-FD4FDE2C28CC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{C57B2480-F632-4691-9C4C-8CC01237203C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{B54DEF90-C30C-44EA-9875-76F1B330CBB7} = {EDF84A84-1E01-484E-B073-383F7139C891}
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{6A80FFEC-9B83-40A7-8C78-124440B48B33} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{13E40DF1-6123-4838-9BF8-086C94E6ADF6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{84B18026-F9A0-4366-BC69-1662D9E7342D} = {EDF84A84-1E01-484E-B073-383F7139C891}
{E009A053-A9F4-48F2-984F-EF5C376A9B14} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{47D80C65-74A2-4EB8-96A5-D571A9108FB3} = {72CBAFBA-55CC-49C9-A484-F8F4550054CB}
{0FA93730-8084-4907-B172-87D610323796} = {EDF84A84-1E01-484E-B073-383F7139C891}
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6} = {EDF84A84-1E01-484E-B073-383F7139C891}
{72CBAFBA-55CC-49C9-A484-F8F4550054CB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{C9266749-9504-4EA9-938F-F083357B60B7} = {72CBAFBA-55CC-49C9-A484-F8F4550054CB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution