entity framework core nested object with self parentId - c#

I have a treetable structure and this data comes to me from the frontend.
In this treetable structure, there is IssueActivity and IssueActivityDetail for details of this issue.
Now my question is, more than one IssueActivityDetail field can be added to this IssueActivity field. How can I do this on the c# ef core side?
I tried to do it with the logic of ParentId. My Entity structures are as follows. I did not add the parentId in FluenApi because I did not fully understand it.
My IssueActivity table.
public partial class IssueActivitiy
{
public int Id { get; set; }
public int IssueId { get; set; }
public byte Type { get; set; }
public short SubActivityNo { get; set; }
public string SubActivityTitle { get; set; }
public virtual Issue Issue { get; set; }
public virtual List<IssueActivitiyDetail> IssueActivitiyDetails { get; set; }
}
My IssueActivityDetail table.
public partial class IssueActivitiyDetail
{
public int Id { get; set; }
public int IssueActivityId { get; set; }
public short LineNo { get; set; }
public string Definition { get; set; }
public byte RoleId { get; set; }
public byte Medium { get; set; }
public string Explanation { get; set; }
public int? ParentId { get; set; }
public virtual IssueActivitiy IssueActivity { get; set; }
}
FluentApi Configuration.
public void Configure(EntityTypeBuilder<IssueActivitiy> modelBuilder)
{
modelBuilder.ToTable("IssueActivitiy");
modelBuilder.HasKey(a => a.Id);
modelBuilder.Property(e => e.SubActivityNo).HasComment("Sıra No");
modelBuilder.Property(e => e.SubActivityTitle).HasMaxLength(256).IsUnicode(false);
modelBuilder.Property(e => e.Type).HasDefaultValueSql("((1))").HasComment("1) Temel Aktivite\r\n2) Alternatif Aktivite\r\n3) İşlem İptal Aktivite");
modelBuilder.HasOne(d => d.Issue).WithMany(p => p.IssueActivitiys).HasForeignKey(d => d.IssueId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_Issue_IssueActivitiy_Id");
}
public void Configure(EntityTypeBuilder<IssueActivitiyDetail> modelBuilder)
{
modelBuilder.ToTable("IssueActivitiyDetail");
modelBuilder.Property(e => e.Definition).IsRequired().HasMaxLength(2048).IsUnicode(false).HasComment("Açıklama");
modelBuilder.Property(e => e.Explanation).HasMaxLength(2048).IsUnicode(false).HasComment("Açıklama");
modelBuilder.Property(e => e.IssueActivityId).HasComment("Konu Id");
modelBuilder.Property(e => e.LineNo).HasComment("Sıra No");
modelBuilder.Property(e => e.Medium).HasComment("Ortam (Excel, Mail vb.)");
modelBuilder.Property(e => e.RoleId).HasComment("Rol");
modelBuilder.Property(e => e.ParentId);
modelBuilder.HasOne(d => d.IssueActivity).WithMany(p => p.IssueActivitiyDetails).HasForeignKey(d => d.IssueActivityId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_IssueActivitiy_IssueActivitiyDetail_");
}
Web Api is also the place where I try to receive and process the data, but I played a lot and couldn't do it correctly.
var vIssueActivity = issueInfo.IssueActivitiyInfos
.Select(a => new IssueActivitiy
{
Type = a.Type,
SubActivityNo = a.SubActivityNo,
SubActivityTitle = a.SubActivityTitle,
IssueActivitiyDetails = a.IssueActivitiyDetailInfos
.Select(x => new IssueActivitiyDetail
{
LineNo = x.LineNo,
Definition = x.Definition,
RoleId = vUser.RoleId,
Medium = x.Medium,
Explanation = x.Explanation,
IssueActivityDetail = new List<IssueActivitiyDetail> { }
}).ToList()
});

You don't need to keep ParentId property in IssueActivityDetail.
public partial class IssueActivitiy
{
...
public virtual List<IssueActivitiyDetail> IssueActivitiyDetails { get; set; }
}
public partial class IssueActivitiyDetail
{
...
public virtual IssueActivitiy IssueActivity { get; set; }
}
Your configuration looks not wrong.
Maybe you can use Include when getting the entity from db context.
var issueActivity = context.IssueActivities.Include(x => x.IssueActivityDetails).FirstOrDefault();

You can accomplish this by retrieving all the entries from the database. Then select the Root node and then let EF Core mapping do the rest.
public class TreeNode
{
public bool IsRoot { get; set; }
public int? ParentNodeId {get; set;}
public virtual List<TreeNode> ChildNodes {get; set;}
}
public class TreeNodeRepository
{
public async Task<TreeNode> GetTreeStructure()
{
var allNodes = await _context.TreeNodes.ToListAsync();
return allNodes.FirstOrDefault(t => t.IsRoot);
}
}
You could argue that ParentId == null would also imply that it's a parent node. this just makes the example given more tuitive imo.
You should consider performance, how many nodes will become an issue, is it exposed through a web-api and would iterating over the nodes be more efficient. So you wouldn't have to load the entire Tree into memory each time but let clients handle that instead.

Related

Entity Framework : reference loop in many to many relationship

I have this three entities Customer, Product and Review.
A customer can have many products, and a product can have only one customer as owner. A customer can also have many reviews, and one review can have only one customer. A product can have many reviews.
It seems like I am having a reference loop and below is the JsonException that I get when trying to get all customers:
Error message
System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.
Path: $.rows.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Product.Reviews.Id.
Code:
namespace Domain.Entities
{
public partial class Customer
{
public int Id { get; set; }
public string? Name { get; set; }
public virtual ICollection<Review> Reviews { get; set; }
}
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Price { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public virtual ICollection<Review> Reviews { get; set; }
}
public partial class Review
{
public int Id { get; set; }
public int Stars { get; set; }
public string Description { get; set; }
public int CustomerId { get; set; }
public int ProductId { get; set; }
public Customer Customer { get; set; }
public Product Product { get; set; }
}
}
ModelBuilder configurations:
// Products configurations
builder.Ignore(e => e.DomainEvents);
builder.HasKey(t => t.Id);
// Customers configurations
builder.Ignore(e => e.DomainEvents);
builder.HasMany(e => e.Reviews)
.WithOne(e => e.Customer)
.HasForeignKey(uc => uc.Id);
builder.HasMany(e => e.MessagesSent)
.WithOne(e => e.Receiver)
.HasForeignKey(uc => uc.SenderId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasMany(e => e.MessagesReceived)
.WithOne(e => e.Sender)
.HasForeignKey(uc => uc.ReceiverId)
.OnDelete(DeleteBehavior.Cascade);
// Reviews configurations
builder.HasKey(t => t.Id);
builder.HasOne(d => d.Customer)
.WithMany(p => p.Reviews)
.HasForeignKey(t => t.CustomerId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(d => d.Product)
.WithMany(p => p.Reviews)
.HasForeignKey(t => t.ProductId)
.OnDelete(DeleteBehavior.Cascade);
Any idea on how to fix this error?
Thanks in advance and if you need any more information please do let me know and I will provide asap.
Edit: this is the query that I am using for getting all customers:
public async Task<PaginatedData<CustomerDto>> Handle(CustomersWithPaginationQuery request)
{
var filters = PredicateBuilder.FromFilter<Customer>("");
var data = await _context.Customers
.Where(filters)
.OrderBy("Id desc")
.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(1, 15);
return data;
}
Edit #2: CustomerDto
namespace Application.Customers.DTOs
{
public partial class CustomerDto : IMapFrom<Customer>
{
public int Id { get; set; }
public string Name { get; set; }
public List<Review> Reviews { get; set; }
}
}
To fix this issue you need to add a ReviewDto class like this:
public partial class ReviewDto
{
public int Id { get; set; }
public int Stars { get; set; }
public string Description { get; set; }
// ...
}
And update the CustomerDto:
public partial class CustomerDto : IMapFrom<Customer>
{
public int Id { get; set; }
public string Name { get; set; }
public List<ReviewDto> Reviews { get; set; }
}
As the comments suggest, the problem is not with EF; it is with the default mechanism of System.Text.Json to serialize everything, even if there are loops. The problem with that is you eventually hit a limit giving you that exception. It is probably not your intent to send such a bloated payload back to API clients.
You can prevent that a number of different ways. You can null out the properties that would lead to cycles, but this "sort of" destroys data and could be misinterpreted by clients.
Another way would be to map your classes with cycles to DTOs that explicitly suppress the loop by not including that data, or substituting a reference property (e.g. an ID or some other reference value) to data that has been repeated.
If you don't want to do that, you can prevent the exception by using a ReferenceHandler set to ignore cycles.
This documentation explains how to do that. The effect is equivalent to the first solution of nulling out the values manually. An excerpt from that page
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
...
Really, though, you're missing a step. It makes more sense to map your returned entities to DTOs. The purpose of the DTOs is to shape the response content to the needs of the API clients. That makes Ghassen's answer a good one.

Many to Many, self join - how to do this, code first in Fluent Api, EF Core 6?

https://i.imgur.com/rvWQVQt.png
So basically, I want to be able to define a User and have them be able to have a list of other Users that I designate as their friends - for some reason I'm stumped
Here are my classes and attempt so far:
public class User : BaseEntity, IUser
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public Guid PhotoId { get; set; }
public string Mobile { get; set; }
public IList<ClubbrEvent> ClubbrEvents { get; set; }
public bool ProfileComplete { get; set; }
public List<UserFriends> Friends { get; set; }
public List<UserFriends> FriendsOf { get; set; }
}
public class UserFriends
{
public Long UserId { get; set; }
public User User { get; set; }
public Long FriendId { get; set; }
public User Friend { get; set; }
}
public class UserFriendsConfiguration: IEntityTypeConfiguration<UserFriends>
{
public void Configure(EntityTypeBuilder<UserFriends> builder)
{
builder.HasOne(f => f.Friend)
.WithMany(fo => fo.FriendsOf)
.HasForeignKey(fk => fk.FriendId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(u => u.User)
.WithMany(f => f.Friends)
.HasForeignKey(fk => fk.UserId);
}
}
But when I try to add a migration I get the following error:
The entity type 'UserFriends' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.
Ok, I figured it out, so leaving this here for anyone else in the same situation.
First, I had made a mistake in my join table properties - I had made them long but they should have been guid
Second, I defined the key in the config like so:
builder.HasKey(k => new { k.UserId, k.FriendId });
So in full:
public void Configure(EntityTypeBuilder<UserFriends> builder)
{
builder.HasKey(k => new { k.UserId, k.FriendId });
builder.HasOne(f => f.Friend)
.WithMany(fo => fo.FriendsOf)
.HasForeignKey(fk => fk.FriendId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(u => u.User)
.WithMany(f => f.Friends)
.HasForeignKey(fk => fk.UserId);
}
Running the migration and update now gives me what I need:
https://i.imgur.com/my674wx.png

EF Core FindAsync returns only root object

I have a many to many relation and I am using ASP.Net Core2.2 MVC with EF.
From what I read I understand that at the current time I have to create a CLR class for join table.
My code:
Models:
public class Post
{
public Post()
{
Categories= new List<PostCategory>();
}
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
public List<PostCategory> Categories { get; set; }
}
public class Category
{
public Category()
{
Posts= new List<PostCategory>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
public ICollection<PostCategory> Posts { get; set; }
}
public class PostCategory
{
public int PostId { get; set; }
public Post Post { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
DBContext:
public class MyDBContext:DbContext
{
public MyDBContext()
{
}
public MyDBContext(DbContextOptions<MyDBContext> options) : base(options)
{
}
public DbSet<Post> Post { get; set; }
public DbSet<Category> Category { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostCategory>()
.HasKey(x => new { x.PostId, x.CategoryId });
modelBuilder.Entity<PostCategory>()
.HasOne(x => x.Post)
.WithMany(m => m.Categories)
.HasForeignKey(x => x.PostId);
modelBuilder.Entity<PostCategory>()
.HasOne(x => x.Category)
.WithMany(e => e.Posts)
.HasForeignKey(x => x.CategoryId);
}
}
Create Post Code:
var categories= new List<PostCategory>();
foreach (var category in model.SelectedCategories)
{
categories.Add(new PostCategory()
{
CategoryId= category
});
}
model.Post.Categories.AddRange(categories);
_context.Post.Add(model.Post);
await _context.SaveChangesAsync();
When I create a post I can see in database that I have Post data, and in PostCategory table I can see PostId CategoryId as they should be.
My issue is when I try to get a post data using:
var post = await _context.Post.FindAsync(id);
post.Categories count is always 0, what am I missing?
You need to write your query as follows to eager load the related Categories with Post:
var post = await _context.Post.Include(p => p.Categories).FirstOrDefaultAsync(p => p.Id == id);
Here is the more details about Loading Related Data in EF core
The accepted answer will work but may not be as performant - when using FindAsync if the entity is already being tracked you'll get that copy rather than going to the database
In order to have that and load related data you need to do it manually
for example:
context.Entry(post)
.Reference(p => p.Categories)
.Load();
references:
https://learn.microsoft.com/en-us/ef/core/querying/related-data/explicit#explicit-loading
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.findasync?view=efcore-7.0#overloads

Many to many relationship mapping in EF Core

I have a problem with many to many relationship in EF core.
I have the following model classes:
public class Meal
{
public int Id { get; set; }
[Required]
public int Insulin { get; set; }
public MealType Type { get; set; }
public ICollection<MealFood> MealFoods { get; set; }
public Meal()
{
MealFoods = new Collection<MealFood>();
}
}
public class Food
{
public int Id { get; set; }
[StringLength(255)]
public string Name { get; set; }
[Required]
public int Carbohydrates { get; set; }
public ICollection<MealFood> MealFoods { get; set; }
public Food()
{
MealFoods = new Collection<MealFood>();
}
}
public class MealFood
{
public int MealId { get; set; }
public Meal Meal { get; set; }
public int FoodId { get; set; }
public Food Food { get; set; }
}
I have the following API resource class:
public class MealResource
{
public int Id { get; set; }
public int Insulin { get; set; }
public MealType Type { get; set; }
public ICollection<FoodResource> Foods { get; set; }
public MealResource()
{
Foods = new Collection<FoodResource>();
}
}
I have done the mapping in my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MealFood>().HasKey(mf => new { mf.MealId, mf.FoodId });
modelBuilder.Entity<MealFood>().HasOne(mf => mf.Meal).WithMany(m => m.MealFoods).HasForeignKey(mf => mf.MealId);
modelBuilder.Entity<MealFood>().HasOne(mf => mf.Food).WithMany(f => f.MealFoods).HasForeignKey(mf => mf.FoodId);
}
I've got a problem with this call:
var meals = await context.Meals.Include(m => m.MealFoods).ToListAsync();
This returns almost everything I need, except the navigation properties from MealFoods
The reason why I want those properties, because I want to do the following mapping:
CreateMap<Meal, MealResource>().ForMember(mr => mr.Foods, opt => opt.MapFrom(x => x.MealFoods.Select(y => y.Food).ToList()));
I have already found this:
Automapper many to many mapping
but (maybe I don't get something) this doesn't work because the property called Food in MealFood is null.
I hope I didn't explain too complex.
When you include navigation property, EF Core automatically fills the inverse navigation property, e.g. including Meal.MealFoods will automatically fill MealFood.Meal, including Food.MealFoods will automatically populate MealFood.Food etc. In order to populate other navigation properties you need to use additional ThenInclude. E.g.
var meals = await context.Meals
.Include(m => m.MealFoods)
.ThenInclude(mf => mf.Food) // <--
.ToListAsync();
or
var foods = await context.Foods
.Include(f => f.MealFoods)
.ThenInclude(mf => mf.Meal) // <--
.ToListAsync();

Code First Complex Type Or Foreign Key Relationship?

I'm writing an MVC5 application with EF6 which allows users to enter job timesheets, job notes and email the customer an update.
I want my database structure to compose of 3 tables for this (Timers, JobNotes and Emails). I can have 3 models like this;
[Table("Timers")]
public partial class TimerModel
{
public int ID { get; set; }
public string User { get; set; }
...etc
public virtual CustomerEmailModel CustomerEmail { get; set; }
}
[Table("JobNotes")]
public partial class JobNoteModel
{
public int ID { get; set; }
[Column("Username")]
public string User { get; set; }
...etc
public virtual CustomerEmailModel CustomerEmail { get; set; }
}
[ComplexType]
public partial class CustomerEmailModel
{
[Display(Name = "Email Customer?")]
public bool SendEmail { get; set; }
[DataType(DataType.EmailAddress)]
public string To { get; set; }
public string Subject { get; set; }
[DataType(DataType.MultilineText)]
public string Body { get; set; }
}
But this obviously doens't create the Email table, and instead adds the properties to the Timer and JobNotes tables (e.g. Email_SendEmail, Email_To, etc).
If i remove the [ComplexType] annotation, and change my other models to have;
public int? EmailID { get; set; }
[ForeignKey("EmailID")]
public virtual CustomerEmailModel CustomerEmail { get; set; }
Then it does create the table, but then i'm unsure of how to add new entries (i.e. emails are added on the fly, with a 0..1 relationship. It would mean that i would need to add to the email explicitly, get the added entries ID, and then assign it to the other model (timer or jobnote model) EmailID.
Is there a better way of doing this?
Thanks for any help in advance. Please let me know if you need any further information.
EDIT:
It seems that I do need to provide more information from the answers I'm getting so far.
I have two databases in play. A vendor supplied database, for an application which records job, timesheet, employee, customer, etc information. Lets call it DB01.
And a database (call it DB02) for my application (its a mobile app for users to record timesheet or jobnote information, and submit it to DB01).
The mobile app has a user interface which has the following key inputs;
Start Time, Stop Time, Break Time, Job Selector (drop down), Timesheet Title, Timesheet Notes, Send Email To Customer (checkbox), To Address, Subject, Body.
As such, a complex type does work correctly (and is what i'm using in the interm). However, because I have another page which will be able to email the customer as well, I wanted a separate email table (however, i don't want to have to save the email separately, and then assign it to the timesheet or jobnote).
Also - the only tables i need (for DB02), are the ones to store the timesheet, jobnote and email data. Once the timesheet or jobnote gets submitted, it can be deleted or archived. I only need these tables, because all other relevant information is contained in DB01. I can retrieve this data from views on DB02, and i can submit the information from DB02 to DB01 with a stored procedure from DB02. (DB02 has DB01 as a linked server)
This is a simple example of the use of foreign keys with EF6
public class A
{
public int Id { get; set; }
public virtual B Bobject { get; set; }
public int BId;
public virtual ICollection<C> Cs { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<A> As { get; set; }
}
}
public class C
{
public int Id { get; set; }
public string TransactionId { get; set; }
public virtual A Aobj { get; set; }
public int AId { get; set; }
}
You have quite big issues with your database to begin with, and you're not clearly defining the different sets of data.
I've redeveloped what you're trying to achieve using the Fluent API.
public class TimeSheet
{
public TimeSheet()
{
this.TimeSheetId = Guid.NewGuid()
.ToString();
}
public virtual Employee Employee { get; set; }
public string EmployeeId { get; set; }
public virtual Job Job { get; set; }
public string JobId { get; set; }
public string TimeSheetId { get; set; }
}
public class Employee
{
public Employee()
{
this.EmployeeId = Guid.NewGuid()
.ToString();
}
public string EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public virtual ICollection<TimeSheet> TimeSheets { get; set; }
public virtual ICollection<Mail> MailsSent { get; set; }
}
public class Job
{
public Job()
{
this.JobId = Guid.NewGuid()
.ToString();
}
// One job will have one client
public virtual Client Client { get; set; }
public string ClientId { get; set; }
public string JobId { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
// A Job may have many time sheets
public virtual ICollection<TimeSheet> TimeSheets { get; set; }
}
public class Client
{
public Client()
{
this.ClientId = Guid.NewGuid()
.ToString();
}
public string ClientId { get; set; }
public string EmailAddress { get; set; }
// A client can have many work packages / jobs.
public virtual ICollection<Job> WorkPackages { get; set; }
public virtual ICollection<Mail> Mails { get; set; }
}
public class Mail
{
public Mail()
{
this.MailId = Guid.NewGuid()
.ToString();
}
// A mail item will reference one client.
public virtual Client Client { get; set; }
public string ClientId { get; set; }
public string MailId { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public string EmployeeId { get; set; }
// A mail item will also originate from an employee
public virtual Employee Employee { get; set; }
// This doesn't belong here... as if it isn't
// being sent, then it wouldn't make sense to create
// create the email in the first place...
// If you want to queue emails, rename the field to `IsSent`
//
// public bool SendEmail { get; set; }
}
public class TimeSheetConfiguration : EntityTypeConfiguration<TimeSheet>
{
public TimeSheetConfiguration()
{
this.ToTable("TimeSheets");
this.HasKey(timeSheet => timeSheet.TimeSheetId);
this.Property(property => property.TimeSheetId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(property => property.JobId) .IsRequired();
this.Property(property => property.EmployeeId) .IsRequired();
this.HasRequired(timeSheet => timeSheet.Job) .WithMany(job => job.TimeSheets).HasForeignKey(timeSheet => timeSheet.JobId);
this.HasRequired(timeSheet => timeSheet.Employee).WithMany(emp => emp.TimeSheets).HasForeignKey(timeSheet => timeSheet.EmployeeId);
}
}
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
this.ToTable("Employees");
this.HasKey(emp => emp.EmployeeId);
this.Property(property => property.EmployeeId) .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(property => property.FirstName) .IsRequired();
this.Property(property => property.LastName) .IsOptional();
this.Property(property => property.EmailAddress).IsRequired();
this.HasMany(employee => employee.TimeSheets).WithRequired(time => time.Employee).HasForeignKey(time => time.EmployeeId);
this.HasMany(employee => employee.MailsSent) .WithRequired(mail => mail.Employee).HasForeignKey(mail => mail.EmployeeId);
}
}
public class ClientConfiguration : EntityTypeConfiguration<Client>
{
public ClientConfiguration()
{
this.ToTable("Clients");
this.HasKey(client => client.ClientId);
this.Property(property => property.ClientId) .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(property => property.EmailAddress).IsRequired();
this.HasMany(property => property.WorkPackages).WithRequired(job => job.Client) .HasForeignKey(job => job.ClientId);
this.HasMany(property => property.Mails) .WithRequired(mail => mail.Client).HasForeignKey(mail => mail.ClientId);
}
}
public class JobConfiguration : EntityTypeConfiguration<Job>
{
public JobConfiguration()
{
this.ToTable("Jobs");
this.HasKey(job => job.JobId);
this.Property(property => property.JobId) .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(property => property.Name) .IsRequired();
this.Property(property => property.ClientId).IsRequired();
this.Property(property => property.Notes) .IsRequired();
this.HasMany(job => job.TimeSheets).WithRequired(time => time.Job) .HasForeignKey(time => time.JobId);
this.HasRequired(job => job.Client).WithMany (client => client.WorkPackages).HasForeignKey(job => job.ClientId);
}
}
public class MailConfiguration : EntityTypeConfiguration<Mail>
{
public MailConfiguration()
{
this.ToTable("Mails");
this.HasKey(mail => mail.MailId);
this.Property(property => property.MailId) .IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(property => property.ClientId) .IsRequired();
this.Property(property => property.EmployeeId).IsRequired();
this.Property(property => property.Subject) .IsRequired();
this.Property(property => property.Body) .IsRequired();
this.HasRequired(mail => mail.Client) .WithMany(client => client.Mails) .HasForeignKey(mail => mail.ClientId);
this.HasRequired(mail => mail.Employee).WithMany(employee => employee.MailsSent).HasForeignKey(mail => mail.EmployeeId);
}
}
public class ExampleContext : DbContext
{
public DbSet<Mail> Mails { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<TimeSheet> TimeSheets { get; set; }
/// <summary>
/// This method is called when the model for a derived context has been initialized, but
/// before the model has been locked down and used to initialize the context. The default
/// implementation of this method does nothing, but it can be overridden in a derived class
/// such that the model can be further configured before it is locked down.
/// </summary>
/// <remarks>
/// Typically, this method is called only once when the first instance of a derived context
/// is created. The model for that context is then cached and is for all further instances of
/// the context in the app domain. This caching can be disabled by setting the ModelCaching
/// property on the given ModelBuidler, but note that this can seriously degrade performance.
/// More control over caching is provided through use of the DbModelBuilder and DbContextFactory
/// classes directly.
/// </remarks>
/// <param name="modelBuilder">The builder that defines the model for the context being created. </param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MailConfiguration());
modelBuilder.Configurations.Add(new ClientConfiguration());
modelBuilder.Configurations.Add(new EmployeeConfiguration());
modelBuilder.Configurations.Add(new TimeSheetConfiguration());
modelBuilder.Configurations.Add(new JobConfiguration());
base.OnModelCreating(modelBuilder);
}
}

Categories