C
C#β€’3mo ago
WilΓΈvy

EF 9

Hi, I am learning C#, EF, gRPC with a small project (A Blog). I am using SQLServer as DB. I have a problem with EF, I want to validate that the user id sent to me really exists so I can't create a post with an id of a user that doesn't exist.
No description
No description
29 Replies
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
// microservices/PostService/Models/Post.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using UserModel.Models;

namespace PostModel.Models
{
public class Post
{
public int Id { get; set; }

[Required]
[ForeignKey("User")]
required public int OwnerId { get; set; }

public User User { get; set; } = null!;

[Required]
[MinLength(1), MaxLength(254)]
required public string Title { get; set; }

public string Description { get; set; } = null!;
public string Image { get; set; } = null!;
}
}
// microservices/PostService/Models/Post.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using UserModel.Models;

namespace PostModel.Models
{
public class Post
{
public int Id { get; set; }

[Required]
[ForeignKey("User")]
required public int OwnerId { get; set; }

public User User { get; set; } = null!;

[Required]
[MinLength(1), MaxLength(254)]
required public string Title { get; set; }

public string Description { get; set; } = null!;
public string Image { get; set; } = null!;
}
}
// microservices/PostService/Models/PostContext.cs
using Microsoft.EntityFrameworkCore;
using UserModel.Models;

namespace PostModel.Models
{
public class PostContext : DbContext
{
public PostContext(DbContextOptions<PostContext> options) : base(options)
{
}

public DbSet<Post> Post { get; set; }
public DbSet<User> User { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
// microservices/PostService/Models/PostContext.cs
using Microsoft.EntityFrameworkCore;
using UserModel.Models;

namespace PostModel.Models
{
public class PostContext : DbContext
{
public PostContext(DbContextOptions<PostContext> options) : base(options)
{
}

public DbSet<Post> Post { get; set; }
public DbSet<User> User { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
// microservices/PostService/Services/ManagePostService.cs
using Grpc.Core;
using PostProtoService;
using PostModel.Models;

namespace PostService.Services;

public class ManagePostService : PostProto.PostProtoBase
{
private readonly ILogger<ManagePostService> _logger;
private readonly PostContext _dbContext;

public ManagePostService(ILogger<ManagePostService> logger, PostContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}

public override async Task<CreateReply> Create(CreateRequest request, ServerCallContext context)
{

var user = _dbContext.User.FirstOrDefault(u => u.Id == request.OwnerId);

if (user != null) throw new RpcException(new Status(StatusCode.NotFound, "El usuario no existe."));

var newPost = new Post
{
OwnerId = request.OwnerId,
Title = request.Title,
Description = request.Description,
Image = request.Image,
};

_dbContext.Post.Add(newPost);
await _dbContext.SaveChangesAsync();

return new CreateReply
{
OwnerId = request.OwnerId,
Image = request.Image,
Title = request.Title,
Description = request.Description
};
}
}
// microservices/PostService/Services/ManagePostService.cs
using Grpc.Core;
using PostProtoService;
using PostModel.Models;

namespace PostService.Services;

public class ManagePostService : PostProto.PostProtoBase
{
private readonly ILogger<ManagePostService> _logger;
private readonly PostContext _dbContext;

public ManagePostService(ILogger<ManagePostService> logger, PostContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}

public override async Task<CreateReply> Create(CreateRequest request, ServerCallContext context)
{

var user = _dbContext.User.FirstOrDefault(u => u.Id == request.OwnerId);

if (user != null) throw new RpcException(new Status(StatusCode.NotFound, "El usuario no existe."));

var newPost = new Post
{
OwnerId = request.OwnerId,
Title = request.Title,
Description = request.Description,
Image = request.Image,
};

_dbContext.Post.Add(newPost);
await _dbContext.SaveChangesAsync();

return new CreateReply
{
OwnerId = request.OwnerId,
Image = request.Image,
Title = request.Title,
Description = request.Description
};
}
}
<!-- microservices/PostService/PostService.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">

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

<ItemGroup>
<Protobuf Include="..\Protos\Posts\post.proto" GrpcServices="Server" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.57.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\UserService\UserService.csproj" />
</ItemGroup>

</Project>
<!-- microservices/PostService/PostService.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">

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

<ItemGroup>
<Protobuf Include="..\Protos\Posts\post.proto" GrpcServices="Server" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.57.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\UserService\UserService.csproj" />
</ItemGroup>

</Project>
Angius
Angiusβ€’3mo ago
No description
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
The problem I have is that at the moment of generating the migration of Post the migration generates in the same file the User table (this table is already created by the other microservice) and at the moment of executing it, it is broken. Any idea how I can solve it?
Angius
Angiusβ€’3mo ago
If user is not null, it means the user exists Ah, microservices... assuming you use a single database, you should have a single project for all your database models and dbcontext Then reference that in each service, or wherever needed
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
I don't understand Isn't each microservice an independent project?
Angius
Angiusβ€’3mo ago
Yeah But the database is its own thing, that backs them all
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
πŸ“¦ NanoStore
β”œβ”€β”€ πŸ“„ angular-cli.nix
β”œβ”€β”€ πŸ“„ flake.lock
β”œβ”€β”€ πŸ“„ flake.nix
β”œβ”€β”€ πŸ“ frontend
β”œβ”€β”€ πŸ“ microservices
β”‚ β”œβ”€β”€ πŸ“ PostService
β”‚ β”‚ β”œβ”€β”€ πŸ“„ appsettings.Development.json
β”‚ β”‚ β”œβ”€β”€ πŸ“„ appsettings.json
β”‚ β”‚ β”œβ”€β”€ πŸ“ bin
β”‚ β”‚ β”œβ”€β”€ πŸ“ Migrations
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241209004606_Post.cs
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241209004606_Post.Designer.cs
β”‚ β”‚ β”‚ └── πŸ“„ PostContextModelSnapshot.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ Models
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ Post.cs
β”‚ β”‚ β”‚ └── πŸ“„ PostContext.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ obj
β”‚ β”‚ β”œβ”€β”€ πŸ“„ PostService.csproj
β”‚ β”‚ β”œβ”€β”€ πŸ“„ Program.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ Properties
β”‚ β”‚ └── πŸ“ Services
β”‚ β”œβ”€β”€ πŸ“ Protos
β”‚ β”‚ β”œβ”€β”€ πŸ“ Posts
β”‚ β”‚ β”‚ └── πŸ“„ post.proto
β”‚ β”‚ └── πŸ“ User
β”‚ β”‚ └── πŸ“„ user.proto
β”‚ β”œβ”€β”€ πŸ“„ startup.sh
β”‚ └── πŸ“ UserService
β”‚ β”œβ”€β”€ πŸ“„ appsettings.Development.json
β”‚ β”œβ”€β”€ πŸ“„ appsettings.json
β”‚ β”œβ”€β”€ πŸ“ bin
β”‚ β”œβ”€β”€ πŸ“ Migrations
β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241204055139_User.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241204055139_User.Designer.cs
β”‚ β”‚ └── πŸ“„ UserContextModelSnapshot.cs
β”‚ β”œβ”€β”€ πŸ“ Models
β”‚ β”‚ β”œβ”€β”€ πŸ“„ User.cs
β”‚ β”‚ └── πŸ“„ UserContext.cs
β”‚ β”œβ”€β”€ πŸ“ obj
β”‚ β”œβ”€β”€ πŸ“„ Program.cs
β”‚ β”œβ”€β”€ πŸ“ Properties
β”‚ β”œβ”€β”€ πŸ“ Services
β”‚ └── πŸ“„ UserService.csproj
└── πŸ“„ README.md
πŸ“¦ NanoStore
β”œβ”€β”€ πŸ“„ angular-cli.nix
β”œβ”€β”€ πŸ“„ flake.lock
β”œβ”€β”€ πŸ“„ flake.nix
β”œβ”€β”€ πŸ“ frontend
β”œβ”€β”€ πŸ“ microservices
β”‚ β”œβ”€β”€ πŸ“ PostService
β”‚ β”‚ β”œβ”€β”€ πŸ“„ appsettings.Development.json
β”‚ β”‚ β”œβ”€β”€ πŸ“„ appsettings.json
β”‚ β”‚ β”œβ”€β”€ πŸ“ bin
β”‚ β”‚ β”œβ”€β”€ πŸ“ Migrations
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241209004606_Post.cs
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241209004606_Post.Designer.cs
β”‚ β”‚ β”‚ └── πŸ“„ PostContextModelSnapshot.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ Models
β”‚ β”‚ β”‚ β”œβ”€β”€ πŸ“„ Post.cs
β”‚ β”‚ β”‚ └── πŸ“„ PostContext.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ obj
β”‚ β”‚ β”œβ”€β”€ πŸ“„ PostService.csproj
β”‚ β”‚ β”œβ”€β”€ πŸ“„ Program.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“ Properties
β”‚ β”‚ └── πŸ“ Services
β”‚ β”œβ”€β”€ πŸ“ Protos
β”‚ β”‚ β”œβ”€β”€ πŸ“ Posts
β”‚ β”‚ β”‚ └── πŸ“„ post.proto
β”‚ β”‚ └── πŸ“ User
β”‚ β”‚ └── πŸ“„ user.proto
β”‚ β”œβ”€β”€ πŸ“„ startup.sh
β”‚ └── πŸ“ UserService
β”‚ β”œβ”€β”€ πŸ“„ appsettings.Development.json
β”‚ β”œβ”€β”€ πŸ“„ appsettings.json
β”‚ β”œβ”€β”€ πŸ“ bin
β”‚ β”œβ”€β”€ πŸ“ Migrations
β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241204055139_User.cs
β”‚ β”‚ β”œβ”€β”€ πŸ“„ 20241204055139_User.Designer.cs
β”‚ β”‚ └── πŸ“„ UserContextModelSnapshot.cs
β”‚ β”œβ”€β”€ πŸ“ Models
β”‚ β”‚ β”œβ”€β”€ πŸ“„ User.cs
β”‚ β”‚ └── πŸ“„ UserContext.cs
β”‚ β”œβ”€β”€ πŸ“ obj
β”‚ β”œβ”€β”€ πŸ“„ Program.cs
β”‚ β”œβ”€β”€ πŸ“ Properties
β”‚ β”œβ”€β”€ πŸ“ Services
β”‚ └── πŸ“„ UserService.csproj
└── πŸ“„ README.md
Angius
Angiusβ€’3mo ago
Say you were not using EF, you'd have some .sql file describing your database structure or something That would be your Data project Just a config for the database, basically
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
And that's not what EF is good for?
Angius
Angiusβ€’3mo ago
It is
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
So my project structure is wrong?
Angius
Angiusβ€’3mo ago
The database entities describe the rows, the context describes the database The project is your equivalent of an .sql file that would also contain all that You would not have partial .sql files in each microservice, each describing 2-3 tables of it, would you? That said, I never used microservices, nor do I intend to touch them with a ten-foot pole unless I have a gun to my head and a multibillion contract So perhaps there are ways to scatter your EF config across multiple projects that I don't know of
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
I said the same thing, but I got the price
Angius
Angiusβ€’3mo ago
But, generally, EF treats each DbContext as a separate database
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
OKay, then I have to find a way to use EF globally?
Angius
Angiusβ€’3mo ago
A separate project
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
This separate project can be my REST API for the JS client?
Angius
Angiusβ€’3mo ago
Skunga
|β€” Skunga.UserService
|β€” Skunga.PostService
|β€” Skunga.TagService
|β€” Skunga.Data <-- ef stuff goes here
Skunga
|β€” Skunga.UserService
|β€” Skunga.PostService
|β€” Skunga.TagService
|β€” Skunga.Data <-- ef stuff goes here
What? No Just a class library
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
Pff I don't understand anything bro, I'm JS dev but I changed my position and now I have to use C#.
Angius
Angiusβ€’3mo ago
Bad idea to jump into a project this complex with no knowledge of ASP and C#
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
This is not yet a job, it is a learning project so as not to arrive at work at 0 in January.
Angius
Angiusβ€’3mo ago
Skunga
|β€” Skunga.UserService
| |β€” Skunga.UserService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.PostService
| |β€” Skunga.PostService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.TagService
| |β€” Skunga.TagService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.Data
| |β€” User.cs
| |β€” Post.cs
| |β€” Tag.cs
| |β€” SkungaDbContext.cs
| \β€” ...etc
\β€” Skunga.sln
Skunga
|β€” Skunga.UserService
| |β€” Skunga.UserService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.PostService
| |β€” Skunga.PostService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.TagService
| |β€” Skunga.TagService.csproj
| |β€” Program.cs
| \β€” ...etc
|β€” Skunga.Data
| |β€” User.cs
| |β€” Post.cs
| |β€” Tag.cs
| |β€” SkungaDbContext.cs
| \β€” ...etc
\β€” Skunga.sln
Separate projects for each service Each project being a WebAPI or whatever Then, a separate Data project for just the database models and the dbcontext A class library Referenced by all the services Then, run migrations on that project
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
Oh okay, so what I have to do is to remove the models and migrations from each gRPC project and put them in a new one called idk PrepareDB that project is going to take care of accommodating all the tables. Then I can run each microservice without any problem.
Angius
Angiusβ€’3mo ago
Yeah
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
Oh, and I can't put this in my api rest? Because these microservices are in gRPC and to connect them to the client (Angular) I will use a REST api. But where is the part where microservices communicate with each other?
Angius
Angiusβ€’3mo ago
Well, microservices communicate with eachother using... something gRPC, REST, whatever There's no, like, Communicator project or whatever Each microservice is an API that calls other APIs, basically If you need to get a, say, blogpost You query the blogpost service, that queries the user service to get author data, and the tag service to get the list of tags One API call turns into three Alternatively, the frontend queries all three services Post service for the post data, user service with the post ID for the author data, and tag service with the post ID for the list of tags
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
But for example, I'm going to create a blog, I have 2 gRPC projects - UserService - PostService I will have one more project to set up the DB - PrepareDB This PrepareDB will be referenced in the 2 gRPC projects. But how do these 2 gRPC projects communicate with each other? Simply with the Both so that they are grpc clients and servers? Although looking at it, I believe that they do not have to communicate between them. Then I will have my REST api where I will also reference that prepareDB project, and here it will also be a gRPC client. To be able to query the microservices And then transform it to REST endopoint
Angius
Angiusβ€’3mo ago
If they need to communicate with eachother, they can do that through gRPC, REST, whatever Well, if the REST api is only supposed to be the glue to the microservices, then it should not need to reference the database project Not to mention, having a glue API like that kinda defeats the point of microservices Well, for starters, microservices are not an architectural solution, they're an organizational one But, by design, you should be able to edit any microservice at any time without impacting anything else If you change what a given service returns... the API will have to change to account for it
WilΓΈvy
WilΓΈvyOPβ€’3mo ago
Okay, I think I understand how to solve it, thank you very much!

Did you find this page helpful?