I'm trying to create a one-to-one mapping with Entity Framework code-first (including fluent API mapping) approach. This is the first time I'm using code first approach.
When I run the UpdateTaskCompleted() method, it throws the following exception:
Operand type clash: uniqueidentifier is incompatible with int
I suspect that I'm doing something wrong in fluent API mapping.
[Table("tblSession")]
public partial class tblSession
{
[Key]
public Guid SessionId { get; set; }
[Required]
public bool IsActive { get; set; }
public tblTaskDetail tblTaskDetail { get; set; }
}
[Table("tblTaskDetail")]
public partial class tblTaskDetail
{
[Key]
public int TaskDetailID { get; set; }
public Guid? SessionID { get; set; }
[Required]
[StringLength(50)]
public string TaskStatus { get; set; }
[ForeignKey("SessionID")]
public tblSession tblSession { get; set; }
}
public class RequestSession
{
[Key]
public Guid SessionId { get; set; }
public bool IsActive { get; set; }
public TaskDetail TaskDetail { get; set; }
}
public class TaskDetail
{
[Key]
public int TaskDetailID { get; set; }
public Guid? SessionID { get; set; }
public string TaskStatus { get; set; }
public RequestSession RequestSession { get; set; }
}
public class TaskDetailMapper:EntityTypeConfiguration<TaskDetail>
{
public TaskDetailMapper()
{
this.ToTable("tblTaskDetail");
this.HasKey(hk => hk.TaskDetailID);
HasRequired<RequestSession>(a => a.RequestSession)
.WithRequiredPrincipal(o => o.TaskDetail).Map(m => m.MapKey("SessionID"));
this.Property(o => o.TaskStatus).HasColumnName("TaskStatus");
}
}
public class RequestSessionMapper : EntityTypeConfiguration<RequestSession>
{
public RequestSessionMapper()
{
// Table & Column Mappings
this.ToTable("tblSession");
//Primary key
this.HasKey<Guid>(hk => hk.SessionId);
this.Property(t => t.SessionId).HasColumnName("SessionId");
this.Property(t => t.IsActive).HasColumnName("IsActive");
}
}
public partial class WarehouseAPIContext : DbContext
{
public WarehouseAPIContext(): base("name=WarehouseAPIContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new RequestSessionMapper());
modelBuilder.Configurations.Add(new TaskDetailMapper());
}
}
public TaskDetail UpdateTaskCompleted(TaskDetail entity)
{
try
{
var entry = dbSet.Find(entity.TaskDetailID);
entry.TaskStatus = entity.TaskStatus;
entity.RequestSession = new RequestSession()
{
IsActive = false
};
_context.SaveChanges();
return entity;
}
catch (Exception ex)
{
throw ex;
}
}
TaskDetail.Id is of type int and Session.Id is of type Guid.
Firstly, I would choose to use either Annotation, or FluentAPI for configuring your model. There are edge-cases where features can only be done in one approach and not the other, but these are rare only a small handful and well documented.
I use FluentAPI as it is more expressive, and allows for all of the configuration to be in one place.
What you need to do here, is check out this very good resource on EF relationships: http://www.entityframeworktutorial.net/entity-relationships.aspx
A google for any entity framework issue/feature will have this site in its top results on the first page - Take some time and do a good bit of research before asking questions - Everyone is more than happy to help with answers, but by researching and reading material looking for the solution to your issues is where the real value will come from, as you will learn a lot more than just how to fix your current issue.
Related
im using entity framework core 5.0 and i created my one to many relationship with fluent api.
im getting that error when i try to create a new user in my project.
let me show u to my User class:
public class User : Base
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string UserSurname { get; set; }
public string UserPassword { get; set; }
public string UserEMail { get; set; }
public int? AgencyID { get; set; }
public virtual Agency Agency { get; set; }
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(user => user.UserID);
}
}
and here its a Agency class which is related to User class:
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
public string AgencyName { get; set; }
public string AgencyPhoto { get; set; }
public string AgencyEMail { get; set; }
public string AgencyPhone { get; set; }
public int AgencyExportArea { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
{
public void Configure(EntityTypeBuilder<Agency> builder)
{
builder.HasKey(agency => agency.AgencyID);
builder.HasMany(us => us.Users)
.WithOne(us => us.Agency)
.HasForeignKey(au => au.UserID)
.IsRequired(false)
}
}
i know,im getting that error SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Users_Agencies_UserID". The conflict occurred in database "anan", table "dbo.Agencies", column 'AgencyID'. because there is a no data in Agency table. The thing which im trying to do is make that AgencyID foreign key optional as a nullable. in User class u can see i defined that AgencyID as a nullable.
do i really need to define that relationship as a one-to-one or zero or is there a another way to do that ?
if i have to define that relationship as a one-to-one or zero,can u show me the way how can i do that.
Since you are using EF core 5 you don't need:
public class UserConfiguration : IEntityTypeConfiguration<User>
and
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
All this code is reduntant. You have a standart one-to-many relation that EF core recognizes and configures by default. Remove all of this code and everything will be fine.
But if you are a student and need to do everything hard way, you can add this reduntant code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasOne(d => d.Agency)
.WithMany(p => p.Users)
.HasForeignKey(d => d.AgencyId)
.OnDelete(DeleteBehavior.ClientSetNull);
});
}
And since you are interested in a configuration, these are another redundant attributes:
public class User : Base
{
[Key]
public int UserID { get; set; }
.....
public int? AgencyID { get; set; }
[ForeignKey(nameof(AgencyId))]
[InverseProperty("Users")]
public virtual Agency Agency { get; set; }
}
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
.....
[InverseProperty(nameof(User.Agency))]
public virtual ICollection<User> Users { get; set; }
}
This is a operation i have done many times in the past using database-first approach. I'm now trying it with code-first using EF Core and i'm failing horribly.
I have the following model:
public class DataMapping
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Model { get; set; }
public string Property { get; set; }
public bool IgnoreProperty { get; set; }
[NotMapped] //<-- I had to add this as the migration was complaining that it did not know what the relation was
public List<DataMappingRelation> DataMappingRelations { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public DateTime? DateModified { get; set; }
}
and a Bridge model that basically creates a relations between two DataMapping items in the same table:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
However this call does not work:
return _context.DataMappings.Where(x => x.Model == type.FullName)
.Include(x=>x.DataMappingRelations)
.ToList();
It does not like the Include and throws a null ref exception.
All i basically need to do here is for a given "DataMapping" get all the related DataMapping items based on the relations in the "DataMappingRelations" table.
Yes i have looked at this answer but again, it is an example of two seperate tables, not a single table bridging on itself.
I suspect i have done all of this wrong. How can i get this to work? All the examples i have found are bridging two seperate tables. this would be bridging the same table.
Its many-to-many with self but your whole configuration looks messy.
So first, your DataMapping model class should contain two list navigation properties for two foreign keys in the DataMappingRelation as follows:
public class DataMapping
{
......
public List<DataMappingRelation> DataMapping1Relations { get; set; }
public List<DataMappingRelation> DataMapping2Relations { get; set; }
.........
}
Now remove [ForeignKey("DataMappingId")] attribute from both DataMapping1 and DataMapping2 foreign keys as follows:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
Then the Fluent API configuration should be as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping1)
.WithMany(dm => dm.DataMapping1Relations)
.HasForeignKey(dmr => dmr.DataMapping1Id)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping2)
.WithMany(dm => dm.DataMapping2Relations)
.HasForeignKey(dmr => dmr.DataMapping2Id)
.OnDelete(DeleteBehavior.Restrict);
}
I have two tables one with a list of clients and the other whether they are active or not. I want to link them Entity Framework, however, I am struggling. The two tables were already setup and have to primary keys or foreign keys.
namespace DataWarehouse.Models
{
public class DatabaseList
{
[Key]
public string STARDB { get; set; }
public int DBClientID { get; set; }
public string ClientName { get; set; }
public DatabaseStatus DatabaseStatus { get; set; }
public ICollection<PayComponents> PayComponents { get; set; }
= new List<PayComponents>();
}
public class DatabaseStatus
{
[Key]
public string STARDB { get; set; }
public string STATUS { get; set; }
public DatabaseList DatabaseList { get; set; }
}
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
}
public DbSet<DatabaseList> DatabaseList { get; set; }
public DbSet<DatabaseStatus> Status { get; set; }
public DbSet<PayComponents> PayComponents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DatabaseList>()
.HasOne(p => p.DatabaseStatus)
.WithOne(i => i.DatabaseList)
.HasForeignKey<DatabaseStatus>(k => k.STARDB);
}
}
}
I was hoping that Entity Framework would see the columns STARDB and notice that it is the same in both tables and match them that way. All I want to is to add the Status column from DatabaseStatus into the Databaselist table.
Thanks.
Managed to figure it out. My database was setup properly. However, I forgot the include statement in my Repository.cs class.
public IEnumerable<DatabaseList> GetAllClients()
{
_logger.LogInformation("Get all clients was called");
var clients = _ctx.DatabaseList
.Include(d => d.DatabaseStatus)
.OrderBy(p => p.ClientName)
.ToList();
return clients;
}
Still new to C# so a bit of learning curve!
My ASP.NET MVC (.NET Core Framework 1.0) code consists of two models:
public class BusLine
{
public int Id { get; set; }
[Column(Order = 0), ForeignKey("RouteA")]
public int RouteAId { get; set; }
[Column(Order = 1), ForeignKey("RouteB")]
public int? RouteBId { get; set; }
public virtual BusLineRoute RouteA { get; set; }
public virtual BusLineRoute RouteB { get; set; }
}
public class BusLineRoute
{
public int Id { get; set; }
[ForeignKey("BusLine")]
public int BusLineId { get; set; }
public virtual BusLine BusLine { get; set; }
}
BusLine model has to have one BusLineRoute object, and may contain one more optional BusLineRoute.
Unfortunately this way of using data annotations doesn't work, running gives this error:
Unable to determine the relationship represented by navigation property 'BusLine.RouteA' of type 'BusLineRoute'. Either manually configure the relationship, or ignore this property from the model.
I have tried using Fluent API this way:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
(...)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<BusLine>().HasOne(a => a.RouteA).WithOne(b => b.BusLine);
builder.Entity<BusLine>().HasOne(a => a.RouteB).WithOne(b => b.BusLine);
}
}
Then I got another error:
(...) value cannot be null. Parameter name: builder.
builder is object of ModelBuilder class.
What am I doing wrong?
Work on Asp.net mvc entity framework,face problem on child entities add/update/delete portion.
Here is my relationships
public class Client
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClientId { get; set; }
public string ClientName { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyAddress2 { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string ContactPerson { get; set; }
public string ContactPersonPhone { get; set; }
public virtual ICollection<Job> Jobs { get; set; }
}
public class Job
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int JobId { get; set; }
public string JobDescription { get; set; }
public decimal EstamationCost { get; set; }
public DateTime EstemateDelevaryTime { get; set; }
public virtual Client Client { get; set; }
}
public class Context : DbContext
{
public DbSet<Job > Jobs { get; set; }
public DbSet<Client> Clients { get; set; }
}
Please take a look at my job entity,it's not contain any parent id just contain the relation ship.
Want to know how to create any entry/update/delete on child entity.I used bellow syntax for create:
[HttpPost]
public async Task<ActionResult> Create(JobManageViewModel model)
{
if (ModelState.IsValid)
{
_unitOfWorkAsync.BeginTransaction();
try
{
var application = model.ToDalEntity();
application.ObjectState = ObjectState.Added;
_jobService.Insert(application);
var changes = await _unitOfWorkAsync.SaveChangesAsync();
_unitOfWorkAsync.Commit();
return RedirectToAction("Index");
}
catch
{
// Rollback transaction
_unitOfWorkAsync.Rollback();
}
}
LoadClientsInViewData();
return View(model);
}
public Job ToDalEntity(Job model)
{
model.JobId = this.JobId;
model.JobDescription = this.JobDescription;
model.EstamationCost = this.EstamationCost;
model.EstemateDelevaryTime = this.EstemateDelevaryTime;
return model;
}
Problem is can not insert client information on Job table.How to insert/update/delete client information on job table
This is a possible approach, but i strongly do not recommend you to do it, because it is really a bad practice, you are going to break SOLID principles, and so on.
But you could do this way:
Configure your relationship tables
I've created two separated classes, one for each table
Client Configuration
public class ClientEntityTypeConfiguration : EntityTypeConfiguration<Client>
{
public ClientEntityTypeConfiguration()
{
HasKey(x => x.ClientId);
Property(x => x.ClientId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.Jobs).WithOptional(x => x.Client);
}
}
Job Configuration
public class JobEntityTypeConfiguration : EntityTypeConfiguration<Job>
{
public JobEntityTypeConfiguration()
{
HasKey(x => x.JobId);
Property(x => x.JobId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(x => x.Client).WithMany(x => x.Jobs);
}
}
On your context class, you set those classes as your configuration:
public class MyContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Job> Jobs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new ClientEntityTypeConfiguration());
modelBuilder.Configurations.Add(new JobEntityTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
The trick to add your already created client to your Job entity, is that you must set a new instance of Client, and set only the Id, and then tell to your Context that this instance is already exists on database.
var clientAlreadyExists = new Client {ClientId = 1};
context.Clients.Attach(clientAlreadyExists);
job.Client = clientAlreadyExists;
context.Jobs.Add(product);
I recommend you to use Repository Pattern, and do not access your context directly from your Controller, and one more time a say this to you, your approach is a Bad Practice.