Entity Framework. Resolve Zero Or One to one use one navigation property - entity-framework

Sorry for my English. I have the following entity:
public class MediaAlbum
{
[Key]
public Guid AlbumId { get; set; }
public string Title { get; set; }
public virtual ICollection<MediaImage> Images { get; set; }
public Guid? CoverId { get; set; }
[ForeignKey("ImageId")]
public virtual MediaImage Cover { get; set; }
}
public class MediaImage
{
[Key]
public Guid ImageId { get; set; }
public string Image { get; set; }
public Guid AlbumId { get; set; }
[ForeignKey("AlbumId")]
public virtual MediaAlbum Album { get; set; }
}
I need map navigation property Cover to Entity 'MediaImage'.
I tried to solve through fluentApi, but it not worked:
modelBuilder.Entity<MediaAlbum>().HasOptional(x => x.Cover).WithOptionalPrincipal()
.Map(x => x.MapKey("ImageId"));

use this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MediaAlbum>().HasMany(x => x.Images)
.WithRequired(x => x.Album).HasForeignKey(x=>x.AlbumId);
modelBuilder.Entity<MediaAlbum>().HasOptional(x => x.Cover);
}
foreign key is CoverId not ImageId:
public class MediaAlbum
{
[Key]
public Guid AlbumId { get; set; }
public string Title { get; set; }
public virtual ICollection<MediaImage> Images { get; set; }
public Guid? CoverId { get; set; }
[ForeignKey("CoverId")]// change to this
public virtual MediaImage Cover { get; set; }
}

Try this:
modelBuilder.Entity<MediaAlbum>()
.HasOptional(x => x.Cover)
.WithRequired(x => x.Album)
.WillCascadeOnDelete();

Related

Migration failed while trying to create a many to many relationship

I am trying to connect two tables with a code first migration. I thought EF would create many to many relationship table itself but I get error "build failed". While building whole project everything works fine. It's just the migration.
Following are my models -
Task:
[Key]
public int Id { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? EndedAt { get; set; }
[Column(TypeName = "text")]
public string CreatedBy { get; set; }
[Required]
[Column(TypeName = "text")]
public string Title { get; set; }
[Required]
[Column(TypeName = "text")]
public string Description { get; set; }
public virtual TaskGroups TaskGroup { get; set; }
public string Status { get; set; }
[Column(TypeName = "text")]
public string WantedUser { get; set; }
TaskGroup:
[Required]
public int Id { get; set; }
[Required]
public string GroupName { get; set; }
public virtual Tasks Tasks { get; set; }
At first I've tried with ICollection<> but I got the same error.
My project is .Net Core 3.
Any ideas?
Edit
Tasks
[Key]
public int Id { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? EndedAt { get; set; }
[Column(TypeName = "text")]
public string CreatedBy { get; set; }
[Required]
[Column(TypeName = "text")]
public string Title { get; set; }
[Required]
[Column(TypeName = "text")]
public string Description { get; set; }
public string Status { get; set; }
[Column(TypeName = "text")]
public string WantedUser { get; set; }
public IList<TaskGroupTask> TaskGroupTask { get; set; }
TaskGroups
[Key]
public int Id { get; set; }
[Required]
public string GroupName { get; set; }
public IList<TaskGroupTask> { get; set; }
TaskGroupTask
public int TaskId { get; set; }
public int TaskGroupId { get; set; }
public Tasks Tasks { get; set; }
public TaskGroups TaskGroups { get; set; }
DbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<TaskGroupTask>(e =>
{
e.HasKey(p => new { p.TaskId, p.TaskGroupId });
e.HasOne(p => p.Tasks).WithMany(t =>
t.TaskGroupTask).HasForeignKey(p => p.TaskId);
e.HasOne(p => p.TaskGroups).WithMany(tg =>
tg.TaskGroupTask).HasForeignKey(p => p.TaskGroupId);
});
}
public DbSet<TaskGroupTask> TaskGroupTask { get; set; }
You will need to create a joining entity, like MyJoiningEntity or TaskGroupTask, whose sole purpose is to create a link between Task and TaskGroup. Following models should give you the idea -
public class Task
{
public int Id { get; set; }
public string Description { get; set; }
public IList<JoiningEntity> JoiningEntities { get; set; }
}
public class TaskGroup
{
public int Id { get; set; }
public string GroupName { get; set; }
public IList<JoiningEntity> JoiningEntities { get; set; }
}
// this is the Joining Entity that you need to create
public class JoiningEntity
{
public int TaskId { get; set; }
public int TaskGroupId { get; set; }
public Task Task { get; set; }
public TaskGroup TaskGroup { get; set; }
}
Then you can configure the relation in the OnModelCreating method of your DbContext class, like -
modelBuilder.Entity<JoiningEntity>(e =>
{
e.HasKey(p => new { p.TaskId, p.TaskGroupId });
e.HasOne(p => p.Task).WithMany(t => t.JoiningEntities).HasForeignKey(p => p.TaskId);
e.HasOne(p => p.TaskGroup).WithMany(tg => tg.JoiningEntities).HasForeignKey(p => p.TaskGroupId);
});
This will define a composite primary key on JoiningEntity table based on the TaskId and TaskGroupId properties. Since this table's sole purpose is to link two other tables, it doesn't actually need it's very own Id field for primary key.
Note: This approach is for EF versions less than 5.0. From EF 5.0 you can create a many-to-many relationship in a more transparent way.
Since I have some time, I've decided to pull all pices of the code in one place. I think it would be very usefull sample how to create code-first many-to-many relations for database tables. This code was tested in Visual Studio and a new database was created without any warnings:
public class Task
{
public Task()
{
TaskTaskGroups = new HashSet<TaskTaskGroup>();
}
[Key]
public int Id { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? EndedAt { get; set; }
[Column(TypeName = "text")]
public string CreatedBy { get; set; }
[Required]
[Column(TypeName = "text")]
public string Title { get; set; }
[Required]
[Column(TypeName = "text")]
public string Description { get; set; }
public string Status { get; set; }
[Column(TypeName = "text")]
public string WantedUser { get; set; }
[InverseProperty(nameof(TaskTaskGroup.Task))]
public virtual ICollection<TaskTaskGroup> TaskTaskGroups { get; set; }
}
public class TaskGroup
{
public TaskGroup()
{
TaskTaskGroups = new HashSet<TaskTaskGroup>();
}
[Required]
public int Id { get; set; }
[Required]
public string GroupName { get; set; }
[InverseProperty(nameof(TaskTaskGroup.TaskGroup))]
public virtual ICollection<TaskTaskGroup> TaskTaskGroups { get; set; }
}
public class TaskTaskGroup
{
[Key]
public int Id { get; set; }
public int TaskId { get; set; }
[ForeignKey(nameof(TaskId))]
[InverseProperty(nameof(TaskTaskGroup.Task.TaskTaskGroups))]
public virtual Task Task { get; set; }
public int TaskGroupId { get; set; }
[ForeignKey(nameof(TaskGroupId))]
[InverseProperty(nameof(TaskTaskGroup.Task.TaskTaskGroups))]
public virtual TaskGroup TaskGroup { get; set; }
}
public class TaskDbContext : DbContext
{
public TaskDbContext()
{
}
public TaskDbContext(DbContextOptions<TaskDbContext> options)
: base(options)
{
}
public DbSet<Task> Tasks { get; set; }
public DbSet<TaskGroup> TaskGroups { get; set; }
public DbSet<TaskTaskGroup> TaskTaskGroups { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Database=Task;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TaskTaskGroup>(entity =>
{
entity.HasOne(d => d.Task)
.WithMany(p => p.TaskTaskGroups)
.HasForeignKey(d => d.TaskId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_TaskTaskGroup_Task");
entity.HasOne(d => d.TaskGroup)
.WithMany(p => p.TaskTaskGroups)
.HasForeignKey(d => d.TaskGroupId)
.HasConstraintName("FK_TaskTaskGroup_TaskCroup");
});
}
}

Entity Framework, configuration for reading and writing

Given the following database model
I have two fluent entity framework configurations, one that works when I read (the setupFields list is set) and the other for writing, which if I use for reading as well, always comes with an empty SetupFields list
[Table("[BALANCE.SETUP]")]
public class SetupEntity
{
[Key]
public Guid Id { get; set; }
public string Title { get; set; }
public virtual ChassisEntity Chassis { get; set; }
public virtual EventEntity Event { get; set; }
public virtual ICollection<SetupFieldEntity> SetupFields { get; set; }
}
[Table("[BALANCE.SETUP.FIELD]")]
public class SetupFieldEntity
{
[Key]
[Column(Order = 0)]
public Guid SetupId { get; set; }
[Key]
[Column(Order = 1)]
public int Sequence { get; set; }
public string Section { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public virtual SetupEntity Setup { get; set; }
}
(can read)
modelBuilder.Entity<SetupEntity>()
.HasMany(x => x.SetupFields)
.WithRequired(x => x.Setup)
.Map(x => x.MapKey("SETUPID"));
(can write)
modelBuilder.Entity<SetupEntity>()
.HasMany(x => x.SetupFields)
.WithRequired()
.HasForeignKey(x => x.SetupId);
If I use the read configuration to read, this is the error I get:
The column name 'SETUPID' is specified more than once in the SET clause. A column cannot be assigned more than one value in the same SET clause. Modify the SET clause to make sure that a column is updated only once. If the SET clause updates columns of a view, then the column name 'SETUPID' may appear twice in the view definition.
UPDATE 1
Just to make things clear, the models are lazy loaded and I'm explicitly inclucing them, so, when using the first connfiguration the models are set as expected:
if (includeFields)
{
x = x.Include(entity => entity.SetupFields);
}
UPDATE 2
Based on the comments bellow I change the mappings to have just this which works when inserting but when reading the child collection is still null:
modelBuilder.Entity<SetupEntity>().HasMany(x => x.SetupFields);
[Table("[BALANCE.SETUP]")]
public class SetupEntity
{
[Key]
public Guid Id { get; set; }
public string Title { get; set; }
public virtual ChassisEntity Chassis { get; set; }
public virtual EventEntity Event { get; set; }
public virtual ICollection<SetupFieldEntity> SetupFields { get; set; }
}
[Table("[BALANCE.SETUP.FIELD]")]
public class SetupFieldEntity
{
[Key]
[Column(Order = 0)]
public Guid SetupId { get; set; }
[Key]
[Column(Order = 1)]
public int Sequence { get; set; }
public string Section { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public virtual SetupEntity Setup { get; set; }
}
UPDATE 3
Based on the comments I completelly removed the attributes from the entities, but the collection property is still null :(
public class SetupEntity
{
public Guid Id { get; set; }
public string Title { get; set; }
public virtual ChassisEntity Chassis { get; set; }
public virtual EventEntity Event { get; set; }
public virtual ICollection<SetupFieldEntity> SetupFields { get; set; }
}
[Table("[BALANCE.SETUP.FIELD]")]
public class SetupFieldEntity
{
public Guid SetupId { get; set; }
public int Sequence { get; set; }
public string Section { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public virtual SetupEntity Setup { get; set; }
}
modelBuilder.Entity<SetupEntity>()
.HasKey(x => x.Id)
.HasMany(x => x.SetupFields)
.WithRequired(x => x.Setup);
modelBuilder.Entity<SetupFieldEntity>()
.HasKey(x => x.SetupId)
.HasKey(x => x.Sequence);

Sequence contains more than one matching element codefirst

I'm getting this error when trying to update relationship (one to one)
with fluent api this are my classes :
public class Organisation
{
[Key]
public int OrganisationId { get; set; }
public string organisationName { get; set; }
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
public virtual ApplicationUser User { get; set; }
[Required]
public string ApplicationUserId { get; set; }
public int? OrganisationDetalisId { get; set; }
public virtual OrganisationDetalis OrDetalis { get; set; }
public virtual ICollection<Aeroa> aeroa { get; set; }
public virtual ICollection<Order> orders { get; set; }
}
public class OrganisationDetalis
{
[Key]
public int OrganisationDetalisId { get; set; }
//remove for clear code
public int OrganisationId { get; set; }
public virtual Organisation organisation { get; set; }
}
public DbSet<Organisation> organisation { get; set; }
public DbSet<OrganisationDetalis> OrDetalis { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<OrganisationDetalis>()
.HasKey(o => o.OrganisationId);
modelBuilder.Entity<Organisation>()
.HasOptional(ad => ad.OrDetalis).WithRequired(oo => oo.organisation);
}
but I keep getting that error when updating view nugget console
where is the problem?

Entity Framework Code First Fluent API One to Many

I have a ProductRequests table. It has a one to one relationship to ProductRequestDepartments. Which works correctly. I want to link ProductRequestDetails (which will have the actual Products (1 or more) of the ProductRequests.
public partial class WP__ProductRequests
{
[Key]
public int RequestId { get; set; }
[Required]
[StringLength(30)]
public string FromLocation { get; set; }
[Required]
public int ToDepartmentId { get; set; }
[StringLength(4000)]
public string Reason { get; set; }
[Required]
[StringLength(50)]
public string CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
[StringLength(50)]
public string CompletedBy { get; set; }
public DateTime? CompletedDate { get; set; }
[Required]
[StringLength(1)]
public string Status { get; set; }
public ICollection<WP__ProductRequestDetails> ProductRequestDetails { get; set; }
public ICollection<WP__ProductRequestDepartments> ProductRequestDepartments { get; set; }
}
public partial class WP__ProductRequestDetails
{
[Key]
public int RequestDetailsId { get; set; }
[Required]
public int RequestId { get; set; }
[StringLength(20)]
public string ItemCode { get; set; }
[StringLength(100)]
public string ItemName { get; set; }
public int? Quantity { get; set; }
[Required]
[StringLength(1)]
public string Approved { get; set; }
public WP__ProductRequests ProductRequest { get; set; }
}
public partial class WP_ProductRequestDepartments
{
[Key]
public int ID { get; set; }
[StringLength(100)]
public string Department { get; set; }
public int? ApprovalManager { get; set; }
[StringLength(60)]
public string Reason { get; set; }
[StringLength(25)]
public string GeneralLedger { get; set; }
}
How do I wire this up in the Fluent API. So far I tried
public virtual DbSet<WP__ProductRequestDepartments> WP__ProductRequestDepartments { get; set; }
public virtual DbSet<WP__ProductRequestDetails> WP__ProductRequestDetails { get; set; }
public virtual DbSet<WP__ProductRequests> WP__ProductRequests { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WP__ProductRequestDetails>()
.Property(e => e.Approved)
.IsFixedLength()
.IsUnicode(false);
modelBuilder.Entity<WP__ProductRequests>()
.Property(e => e.Status)
.IsFixedLength()
.IsUnicode(false);
modelBuilder.Entity<WP__ProductRequests>()
.HasRequired(a => a.ProductRequestDepartments)
.WithMany()
.HasForeignKey(a => a.ToDepartmentId);
//??
modelBuilder.Entity<WP__ProductRequests>()
.HasRequired(a => a.ProductRequestDetails)
.WithMany()
.HasForeignKey(a => a.RequestId);
}
ProductRequests -> ProductRequestDepartments works correctly (1 : 1)
ProductRequests -> ProductRequestDetail does NOT work (1 : N)
I'm getting
One or more validation errors were detected during model generation:"
WP__ProductRequests_ProductRequestDetails_Source: : Multiplicity is not valid in Role 'WP__ProductRequests_ProductRequestDetails_Source' in relationship 'WP__ProductRequests_ProductRequestDetails'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
I believe you are looking for
modelBuilder.Entity<WP__ProductRequestDetails>()
.HasRequired(productRequestDetails => productRequestDetails.ProductRequest)
.WithMany(productRequest => productRequest.ProductRequestDetails)
.HasForeignKey(productRequestDetails => productRequestDetails.RequestId);

Entity Framework Code First and Invalid Object Name Error

I have a composite table called ImporterState, that are tied to a table called Importer and State. The error happens here context.Importers.Include(q => q.States). Why is this happening?
{"Invalid object name 'ImporterStates'."}
[Table("HeadlineWebsiteImport", Schema = "GrassrootsHoops")]
public class Importer
{
public int Id { get; set; }
public string Name { get; set; }
public string RssUrl { get; set; }
public string Type { get; set; }
public string Keywords { get; set; }
public bool Active { get; set; }
public DateTime DateModified { get; set; }
public DateTime DateCreated { get; set; }
public int WebsiteId { get; set; }
public HeadlineWebsite Website { get; set; }
[InverseProperty("Importers")]
public ICollection<State> States { get; set; }
}
[Table("State", Schema = "GrassrootsHoops")]
public class State
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Abbr { get; set; }
[InverseProperty("States")]
public ICollection<Headline> Headlines { get; set; }
[InverseProperty("States")]
public ICollection<Importer> Importers { get; set; }
}
The many to many is not possible using attributes only.
try using something like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Importer>()
.HasMany(i => i.States)
.WithMany(s => s.Importers)
.Map(m =>
{
m.MapLeftKey("ImporterId");
m.MapRightKey("StateId");
m.ToTable("ImporterState");
});
}