I'm trying to create a one-to-many map, I tried a lot ways to do that, but I just I haven't figured it out yet :/
I have 2 entites, Wallet and Transfer, I want to add in Transfer a FK WalletId, so one transfer has just one wallet, but a wallet can be related to more than one transfer.
Wallet.cs -
public class Wallet
{
public int Id { get; private set; }
public decimal Balance { get; private set; }
}
Transfer.cs -
public class Transfer
{
#region Properties
public int Id { get; private set; }
public decimal Value { get; private set; }
public DateTime? TransferDate { get; private set; }
public DateTime RegisterDate { get; private set; }
public ETransferType TransferType { get; private set; }
}
WalletMap.cs -
public class WalletMap : IEntityTypeConfiguration<Wallet>
{
public void Configure(EntityTypeBuilder<Wallet> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Balance).HasColumnType("Money").IsRequired();
}
}
TransferMap.cs -
public class TransferMap : IEntityTypeConfiguration<Transfer>
{
public void Configure(EntityTypeBuilder<Transfer> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Value).HasColumnType("Money").IsRequired();
builder.Property(x => x.TransferDate);
builder.Property(x => x.RegisterDate).IsRequired();
builder.Property(x => x.TransferType).IsRequired();
}
}
Add a foreign key and navigation property:
public class Wallet
{
public int Id { get; set; }
public decimal Balance { get; set; }
public virtual ICollection<Transfer> Transfers { get; set; } // Navigation Property
}
public class Transfer
{
pubic int Id { get; private set; }
public decimal Value { get; private set; }
public Datetime? TransferDate { get; private set; }
//.....Remaining properties
public int WalletId { get; set; } //Foreign Key
public virtual Wallet Wallet { get; set; } //Reference Navigation
}
This will add the foreign key and navigation properties needed.
//using fluent api
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transfer>().HasOne(x => x.Wallet)
.WithMany(x => x.Transfers)
.HasForeignKey(x => x.WalletId);
modelBuilder.Entity<Wallet>.HasMany(x => x.Transfers)
.WithOne();
}
To Wallet.cs add:
public virtual ICollection<Transfer> Tranfers { get; set; }
To Transfer.cs add:
public virtual Wallet Wallet { get; set; }
To TransferMap.cs add in your Configure block:
builder.HasRequired(x => x.Wallet).WithMany(x => x.Transfers).Map(x => x.MapKey("WalletId")).WillCascadeOnDelete();
You can remove the WillCascadeOnDelete() if you have not configured a cascading delete in the database. This assumes you already have WalletId defined in your Transfers table in the database.
Related
I am following Microsoft's many-to-many ef core example at
https://learn.microsoft.com/en-us/ef/core/modeling/relationships#many-to-many
But get a self referencing loop error.
Here are my entities:
public class Card
{
public Guid Id { get; set; }
public string CardNumber { get; set; }
public CardType CardType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int PassCode { get; set; }
public List<CardSet> CardSets { get; set; }
public Card()
{
CardSets = new List<CardSet>();
}
}
public class Set
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<CardSet> CardSets { get; set; }
public Set()
{
CardSets = new List<CardSet>();
}
}
// join entity
public class CardSet
{
public Guid SetId { get; set; }
public Set Set { get; set; }
public Guid CardId { get; set; }
public Card Card { get; set; }
}
Here is my OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CardSet>().HasKey(cs => new {cs.CardId, cs.SetId});
modelBuilder.Entity<CardSet>()
.HasOne(cs => cs.Card)
.WithMany(c => c.CardSets)
.HasForeignKey(cs => cs.CardId);
modelBuilder.Entity<CardSet>()
.HasOne(cs => cs.Set)
.WithMany(s => s.CardSets)
.HasForeignKey(cs => cs.SetId);
}
Here is the call to get the Set with its Cards:
public Set GetSetWithCards(Guid setId)
{
return context
.Sets
.Include(s => s.CardSets)
.ThenInclude(cs => cs.Card)
.FirstOrDefault(s => s.Id == setId);
}
The error:
Newtonsoft.Json.JsonSerializationException: Self referencing loop
detected for property 'set' with type
'Tools.Entities.Set'. Path 'cardSets[0]'.
All of your entity configurations are correct, and, based on the error message, it appears that the issue is happening when you try to serialize the resulting data to JSON.
Check out this answer for details: JSON.NET Error Self referencing loop detected for type
I'm have a issue,
i'm studyng nhibernate with c# and .net core, but i dont know how to procede on this case.
i create my table with 2FK:
public class Venda
{
public Venda()
{
VendaId = Guid.NewGuid();
DataVenda = DateTime.Now;
}
public virtual Guid VendaId { get; set; }
public virtual Guid BombomId { get; set; }
public virtual Guid ClienteId { get; set; }
public virtual DateTime DataVenda { get; set; }
public virtual IList<Bombom> Bomboms { get; set; }
public virtual IList<Cliente> Clientes { get; set; }
}
My migration is
[FluentMigrator.Migration(2)]
public class CreateTableVenda : FluentMigrator.Migration
{
public override void Up()
{
Create.Table("Venda")
.WithColumn("VendaId").AsGuid().NotNullable().PrimaryKey().Indexed()
.WithColumn("DataVenda").AsDateTime().NotNullable()
.WithColumn("BombomId").AsGuid().NotNullable().Indexed()
.WithColumn("ClienteId").AsGuid().NotNullable().Indexed();
Create.ForeignKey().FromTable("Venda").ForeignColumn("BombomId")
.ToTable("Bombom").PrimaryColumn("BombomId");
Create.ForeignKey().FromTable("Venda").ForeignColumn("ClienteId")
.ToTable("Cliente").PrimaryColumn("ClienteId");
}
public override void Down()
{
Delete.Table("Venda");
}
}
But my mapping, i'm have issues, because i want to LAZYLOAD the map entity together when i get
public class VendaMap : ClassMap<Venda>
{
public VendaMap()
{
Id(x => x.VendaId);
Map(x => x.DataVenda);
Map(x => x.BombomId).Column("Bombom").Access.CamelCaseField();
Map(x => x.ClienteId);
LazyLoad();
}
}
Could someone help me please?
I'm learning ASP.NET Core with Entity Framework and I'm trying to add an FK in my UserDetails table. These are the model:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual UserDetails UserDetail { get; set; }
}
public class UserDetails
{
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}
The table User is the Master table which contains all the registered user in my application (I'm using AspNetCore.Identity).
Actual I want add as FK the property UserId which must bound the Id of User. So inside the ApplicationContext class I did the following:
public class DemoAppContext : IdentityDbContext<ApplicationUser>
{
public DemoAppContext(DbContextOptions<DemoAppContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey(d => d.???; <- problem here
});
}
public DbSet<User> Users { get; set; }
public DbSet<UserDetails> UserDetails { get; set; }
}
I overrided the OnModelCreating and using the ModelBuilder I defined for UserDetails table the MaxLength of some properties. In the last line of builder.Entity<UserDetails> I tried to assign the FK creating the relationship with HasOne => UserKey which contains the object User. The relationship is 1 to 1 so I used WithOne and assigned UserDetail which contains the UserDetails object.
At the end I used HasForeignKey but when I type d. the compiler doesn't show any properties.
What I did wrong? Maybe I overcomplicated the things?
Sorry for any errors, and thanks in advance for any explanation.
The following code will work:
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<UserDetails>(x => x.UserId); //???; < -problem here
});
Can you try this:
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<User>(b => b.Id);
or
public class UserDetails
{
[ForeignKey(nameof(UserKey))]
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}
In my C# project, I get an error when EF attempts to create my database
The error occurs when I call
Database.SetInitializer(new CreateDatabaseIfNotExists<ApplicationDatabase>());
The error message is
The expression 'x => x.Dependancies' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.
My Domain classes are as follows
[Table("LoggedEntity")]
public class LoggedEntity
{
public int Id { get; set; }
}
[Table("TemplateTaskDependancy")]
public class TemplateTaskDependancy : LoggedEntity
{
[Column]
public int NeededTaskId { get; set; }
[Column]
public int TaskId { get; set; }
[Required]
[ForeignKey("TaskId")]
public virtual TemplateTask Task { get; set; }
[Required]
[ForeignKey("NeededTaskId")]
public virtual TemplateTask NeededTask { get; set; }
}
[Table("TemplateTask")]
public class TemplateTask : LoggedEntity
{
public ICollection<TemplateTaskDependancy> Dependancies;
public ICollection<TemplateTaskDependancy> NeededTasks;
public virtual Template Template { get; set; }
}
[Table("Template")]
public class Template : LoggedEntity
{
public string Description { get; set; }
}
My Configuration Class is as follows
public class TemplateTaskConfiguration : EntityTypeConfiguration<TemplateTask>
{
public TemplateTaskConfiguration()
{
HasMany(x => x.Dependancies)
.WithRequired(d => d.Task)
.HasForeignKey(d => d.TaskId)
.WillCascadeOnDelete(false);
HasMany(x => x.NeededTasks)
.WithRequired(d => d.NeededTask)
.HasForeignKey(d => d.NeededTaskId)
.WillCascadeOnDelete(false);
}
}
My Context is as follows
public class ApplicationDatabase : DbContext
{
public DbSet<TemplateTask> TemplateTasks { get; set; }
public DbSet<TemplateTaskDependancy> TemplateTaskDependancies { get; set; }
public DbSet<Template> Templates { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Configurations.Add(new TemplateTaskConfiguration());
}
public void InitializeDatabase()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<ApplicationDatabase>());
}
}
Quite literally, the problem is what is described. Dependancies is a field, not a property. Just define it as:
public virtual ICollection<TemplateTaskDependancy> Dependancies
{
get;
set;
}
And it should solve your problem.
I've got the following model and I want ShiftRequest and MissionRequest to have a single table in the DB.
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestDate { get; set; }
public int UserId { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
}
public class ShiftRequest : RequestBase
{
public virtual Column Column { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
I've tried to do it in the override void OnModelCreating(ModelBuilder modelBuilder) method but only one RequestBases table is created:
modelBuilder.Entity<ShiftRequest>().MapSingleType().ToTable("dbo.ShiftRequests");
modelBuilder.Entity<MissionRequest>().MapSingleType().ToTable("dbo.MissionRequest");
What am I doing wrong?
EDIT
Column and Mission are also entities in my model, is that acceptable?
Check the section about TPH in this article. If Mission and Column are complex types you will also find there how to map them. Generally you have to use MapHiearchy and Case methods instead of MapSingleType.
Edit:
Here is the example:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
namespace EFTest
{
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestedDate { get; set; }
public int UserId { get; set; }
}
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<MissionRequest> MissionRequests { get; set; }
}
public class Column
{
public string Name { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
public class ShiftRequest : RequestBase
{
public Column Column { get; set; }
}
public class TestContext : DbContext
{
public DbSet<RequestBase> Requests { get; set; }
public DbSet<Mission> Missions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ContainerName = "EFTest";
modelBuilder.IncludeMetadataInDatabase = false;
// Example of complex type mapping. First you have to define
// complex type. Than you can access type properties in
// MapHiearchy.
var columnType = modelBuilder.ComplexType<Column>();
columnType.Property(c => c.Name).HasMaxLength(50);
modelBuilder.Entity<Mission>()
.Property(m => m.Id)
.IsIdentity();
modelBuilder.Entity<Mission>()
.HasKey(m => m.Id)
.MapSingleType(m => new { m.Id, m.Name })
.ToTable("dbo.Missions");
modelBuilder.Entity<RequestBase>()
.Property(r => r.Id)
.IsIdentity();
// You map multiple entities to single table. You have to
// add some discriminator to differ entity type in the table.
modelBuilder.Entity<RequestBase>()
.HasKey(r => r.Id)
.MapHierarchy()
.Case<RequestBase>(r => new { r.Id, r.RequestedDate, r.UserId, Discriminator = 0 })
.Case<MissionRequest>(m => new { MissionId = m.Mission.Id, Discriminator = 1 })
.Case<ShiftRequest>(s => new { ColumnName = s.Column.Name, Discriminator = 2 })
.ToTable("dbo.Requests");
}
}
}
Edit 2:
I updated example. Now Mission is entity instead of complex type.