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.
Related
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!
One company can have many addresses, however each company has a main address.
I am looking to find the best way to create this kind of relation in EF Core.
Below is what I came up with. Is there a better way? Am I way off entirely?
Models
public class Company
{
public int Id { get; set; }
public int MainAddressId { get; set; }
public Address MainAddress { get; set; }
public ICollection<CompanyAddress> CompanyAddresses { get; set; }
// other company info
}
public class Address
{
public int Id { get; set; }
public int CompanyAddressId { get; set; }
public CompanyAddress CompanyAddress { get; set; }
// other address info
}
public class CompanyAddress
{
public int CompanyId { get; set; }
public Company Company { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
public bool IsMain { get; set; }
}
DataContext.cs
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<Company> Companies { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<CompanyAddress> CompanyAddresses { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<CompanyAddress>()
.HasKey(ca => new {ca.CompanyId, ca.AddressId});
builder.Entity<CompanyAddress>()
.HasOne(ca => ca.Company)
.WithMany(ca => ca.CompanyAddresses)
.HasForeignKey(ca => ca.CompanyId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<CompanyAddress>()
.HasOne(ca => ca.Address)
.WithOne(ca => ca.CompanyAddresses)
.HasForeignKey(ca => ca.AddressId)
.OnDelete(DeleteBehavior.Cascade);
}
}
In my opinion, dead on. There are always other ways. But this is straight-forward and easily understood. MainAddress and MainAddressId are redundant. You don't have lazy loading (virtual) so you can easily determine the main address by
dbContext.Companies.FirstOrDefault(p => p.Id = <myCompanyId>);
dbContext.CompanyAddresses.FirstOrDefault(p => p.CompanyId == <myCompanyId> && p.IsMain);
If you go with lazy loading later, just add .Include("Address") to the second query. And yes, you can combine the two.
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.
I'm trying to make a enitity that manages membership of a user in a organization with a role. I want to restrict a user to have only one membership in an organization. I'm doing this by creating a composite key. However i get the error when i try to create the initial migrations:
InvalidOperationException: The property 'User' cannot be added to the entity type 'OrganizationLogin' because a navigation property with the same name already exists on entity type 'OrganizationLogin'.
The entity for membership
public class OrganizationLogin
{
public int OrganizationLoginId { get; set; }
public OrganizationRole Role { get; set; }
public Organization Organization { get; set; }
public OmegaUser User { get; set; }
}
My DBContext where I try to define the composite key:
public class OmegaContext : IdentityDbContext<OmegaUser,OmegaRole,int>
{
public DbSet<Log> Logs { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<OrganizationLogin> OrganizationLogins { get; set; }
public DbSet<OrganizationRole> OrganizationRoles { get; set; }
public OmegaContext()
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<OrganizationLogin>(orgLogin =>
{
orgLogin.HasAlternateKey(o => new {o.User, o.Organization});
});
}
}
If i remove the OnModelCreating code, the migrations are created succesfully.
EDIT: As mentioned in the comments, the problem was that i was referencing the class and not a property that had the key of the entities
As requested, here is my solution:
public class OrganizationUnitMember
{
public int OrganizationUnitMemberId { get; set; }
public int UserId { get; set; }
public int OrganizationUnitId { get; set; }
[ForeignKey("UserId")]
public virtual OmegaUser User { get; set; }
[ForeignKey("OrganizationUnitId")]
public virtual OrganizationUnit OrganizationUnit { get; set; }
public int RoleId { get; set; }
[ForeignKey("RoleId")]
public virtual OrganizationRole Role { get; set; }
}
And the DbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationUnit>(
orgUnit =>
{
orgUnit.HasOne(ou => ou.Parent)
.WithMany(ou => ou.Children)
.OnDelete(DeleteBehavior.Restrict)
.HasForeignKey(ou => ou.ParentId);
});
builder.Entity<OrganizationUnitMember>(member =>
{
member.HasAlternateKey(m => new {m.OrganizationUnitId, m.UserId});
});
}
I had to add the ids of the referenced entities
I'm trying to add some entities using EntityFramework. I need the same model as in image
I created 3 classes:
public class UsersOrders : Entity
{
public int Order_ID { get; set; }
public int User_ID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
public class User : Entity
{
public int User_ID { get; set; }
public string Surname { get; set; }
public string Name { get; set; }
public string Patronymic { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<UsersOrders> Orders { get; set; }
}
public class Order : Entity
{
public int Order_ID { get; set; }
public virtual Address Address { get; set; }
public virtual User User_ID_Courier { get; set; }
public virtual ICollection<UsersOrders> Users { get; set; }
}
Using FluentAPI trying to set primary keys in my DBContext:
builder.Entity<UsersOrders>()
.HasKey(od => new {od.Order_ID});
It works, but why in DB this field "Order_Order_ID" appears? I'm not sure but I really didn't set this field.
Thanks for any help!
UPD: OnModelCreating
protected override void OnModelCreating(DbModelBuilder builder)
{
//OrderedDishes
builder.Entity<OrderedDishes>().HasKey(od => new { od.Order_ID, od.Dish_ID, od.Number });
builder.Entity<OrderedDishes>().HasRequired(od => od.Order).WithMany(od => od.Dishes).HasForeignKey(od => od.Order_ID);
builder.Entity<OrderedDishes>().HasRequired(od => od.Dish).WithMany(od => od.Orders).HasForeignKey(od => od.Dish_ID);
//OrderStatus
builder.Entity<OrderStatus>().HasKey(os => new { os.Order_ID, os.StatusType_ID });
builder.Entity<OrderStatus>().HasRequired(os => os.Order);
builder.Entity<OrderStatus>().HasRequired(os => os.StatusType);
//DishStatus
builder.Entity<DishStatus>().HasKey(os => new { os.Order_ID, os.Dish_ID, os.Number, os.StatusType_ID });
builder.Entity<DishStatus>().HasRequired(os => os.OrderedDishes);
builder.Entity<DishStatus>().HasRequired(os => os.StatusType);
//user
builder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID });
builder.Entity<UsersOrders>().HasRequired(os => os.User);
builder.Entity<UsersOrders>().HasRequired(os => os.Order);
//PriceOfDish
builder.Entity<PriceOfDish>().HasKey(t => new { t.Dish_ID, t.DateTime });
}
You have the wrong field because you rely on the Code First's convention but your are not following it correctly for the UsersOrders entity. Assuming that Id is the primary key of User class then it will look for UserId (not User_Id) which is not in your UsersOrders entity. The same goes for Order navigational property in UsersOrders.
To fix this you have to follow the convetion by refactoring your UsersOrders and use data annotations like this: (If you use this solution you must remoeve configuration for UsersOrders entity in your OnModelCreating implementation.
public class UsersOrders
{
[Key]
public int OrderID { get; set; }
[Key]
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
Or in your OnModelCreating implementation make the following changes for UsersOrders entity like this:
modelBuilder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID, od.User_ID });
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.User).WithMany(p => p.Orders).HasForeignKey(p => p.User_ID);
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.Order).WithMany(p => p.Users).HasForeignKey(p => p.Order_ID);
In the two solutions, note that UsersOrders use composite keys using Order_Id and User_Id not only Order_ID.