HasKey: error while using Fluent API to configure which property should be used as the foreign key - asp.net-core

I have two classes (Parent, Child) in ASP.Net Core and I'm using code first approach, my real project is more complex than that, so i have to use this method to migrate to database.
The issue here is when I'm defining the relations in Db Context class i face this error noting that I'm following this Microsoft document https://learn.microsoft.com/en-us/ef/core/modeling/relationships, and you can find the main class below:
Error: Cannot implicitly convert type 'System.Collections.Generic.List<logintest.Models.Chlid>' to 'System.Collections.Generic.IEnumerable<logintest.Models.Child>'. An explicit conversion exists (are you missing a cast?)
public class Parent
{
public int Id { get; set; }
public List<Chlid> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Childs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>()
.HasOne(p => p.Parent)
.WithMany(b => b.Childs)
.HasForeignKey(b => b.ParentId);
}
}

Related

How to use Automapper in ASP.NET Core to create mapping configuration for the following type of hierarchy

I would like to map the following hierarchy of entities to the TestViewModel class. I have TestViewModel class with the same member names and perhaps I will add more members to the view model. I am using AutoMapper.
public class TestProfile : Profile
{
public TestProfile ()
{
CreateMap ??????
}
}
public class Test
{
public List<Test1> Tests1 { get; set; }
public int TestId { get; set; }
}
public class Test1
{
public int Test1Id { get; set; }
public string Name { get; set; }
public List<Document> Documents { get; set; }
}
public class Document
{
public int DocumentId { get; set; }
public DateTimeOffset? ChangeDate { get; set; }
public List<Payload> Payloads { get; set; }
}
public class Payload
{
public string PayloadName { get; set; }
}
You didn't tell us what your TestViewModel class looks like, and whether you also have DocumentViewModel, PayloadViewModel, etc. Typically if you are mapping to a another set of classes that have the same naming and structure, like a set of ViewModels, you will want to have a configuration like this:
public class TestProfile : Profile
{
public TestProfile()
{
CreateMap<Test, TestViewModel>();
CreateMap<Test1, Test1ViewModel>();
CreateMap<Document, DocumentViewModel>();
CreateMap<Payload, PayloadViewModel>();
}
}
This will map all like-named properties between the two sets of classes. If your TestViewModel shares the same child entities, then you only need the first line.

applying an object with a ICollection<Enum> type

In an ASP.NET 3.1 CORE project, using EF, I am trying to implement an object that holds a type of ICollection<Enum> type.
the problem is after reading some tutorials and trying to migrate it to my database something seems off, I will attach screenshots and code for more understanding.
this is the object class :
public class UsersCredentialsModel
{
[Key]
public string UserId { get; set; }
public ICollection<ServiceModel> Services { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Hash { get; set; }
}
The ServiceModel class:
public class ServiceModel
{
[Key]
public string ServiceId { get; set; }
public Service Service { get; set; }
}
The Service Enum Class:
public enum Service : int
{
Badoo = 0,
Tinder = 1,
Grinder = 2,
OkCupid = 3
}
This is the AppDbContext class:
public class AppDbContext : IdentityDbContext<ApplicationUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Message>().Property(m => m.Service).HasConversion<int>();
builder.Entity<ApplicationUser>().HasMany<Message>(m => m.Messages).WithOne(u =>
u.User).IsRequired();
builder.Entity<ServiceModel>().Property(m => m.Service).HasConversion<int>();
builder.Entity<UsersCredentialsModel>().HasMany(s => s.Services);
base.OnModelCreating(builder);
}
public DbSet<UsersCredentialsModel> UsersCredentialsModels { get; set; }
public DbSet<ServiceModel> ServiceModel { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<CookieModel> CookieModel { get; set; }
public DbSet<ProjectionModel> ProjectionModel { get; set; }
}
This is a picture of the UsersCredentialsModel database schema:
** I believe that there should be a filed called "ServiceId" corresponding to the Id of the second table.
and finally a picture of the ServiceModel schema:
from what I understood you can't implement ICollection of type ENUM and you have to wrap it in a class so basically you need an object to hold the ENUM with an ID and another Id that holds the userId.
The problem is that UserCredentialsModel table should hold an Id property of ServiceId coming from ServiceModel table.
because the class has a field of ICollection but when migrating it does nothing

Entity framework code first, delete childs by updating parent

As entity framework states, "Code first", here we go with the code first...
public class BaseModel
{
[Key]
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public BaseModel()
{
this.Id = Guid.NewGuid();
this.DateCreated = DateTime.Now;
this.DateChanged = DateTime.Now;
}
}
public class Association: BaseModel
{
public string Name { get; set; }
public string Type { get; set; }
public virtual List<Rule> Rules { get; set; }
public Association()
: base()
{
}
}
public class Rule: BaseModel
{
[ForeignKey("Association")]
public Guid AssociationId { get; set; }
//[Required]
public virtual Association Association { get; set; }
//[Required]
public string Name { get; set; }
public string Expression { get; set; }
public virtual List<Action> Actions { get; set; }
public Rule()
: base()
{
}
}
public class Action: BaseModel
{
public string Name { get; set; }
public string ActionType { get; set; }
[ForeignKey("Rule")]
public Guid RuleId { get; set; }
public virtual Rule Rule { get; set; }
public int Order { get; set; }
public Action()
: base()
{
}
}
So these are my four model classes that are using entity framework code first.
Each inherit from the baseclass, so they all have an Id Guid as Primary Key.
An Association has a list of rules. (Rule has FK to Association)
A Rule as has a list of actions. (Action has FK to Rule)
What I would like to do is only change and save the most upwards class = Association.
For example when deleting a rule, I would like this code to work:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
Association ass = this.DataContext.Associations.FirstOrDefault(a => a.Id == assId);
ass.Rules.RemoveAll(r => r.Id == ruleId);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
On the context.savechanges this is giving me this error:
'The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
This error also occurs when deleting an action.
Is there a way to change the most upper (Association) object AND ONLY changing things to this Association.
I DO NOT want to say context.Rules.remove(...) or context.actions.remove(...)
here's the source: http://server.thomasgielissen.be/files/mvctesting.zip
you need VS2012, all nuget packages are included in zip and you should be able to build and run the project.
Thanks in advance for your feedback!
Greetz,
Thomas
I you want to fix this issue, you should store your relations through junction tables. I don't think that you can achieve what you need, with this model.
However if you put a junction table(or entity) between your entities, you can easily remove child objects and update parent object.
For example, put a junction entity between Association and Rule:
public class AssociationRule: BaseModel
{
public Guid AssociationId { get; set; }
public Guid RuleId { get; set; }
[ForeignKey("AssociationId")]
public virtual Association Association { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
public Association()
: base()
{
}
}
Now, you can easily remove any rule from any association:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
AssociationRule assr = this.DataContext
.AssociationRuless
.FirstOrDefault(ar => ar.AssociationId == assId && ar.RuleId == ruleId);
this.DataContext.AssociationRules.Remove(assr);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}

Entity Framework Code First Many-to-Many relationship and inheritance

Forgive me if this question has been answered somewhere, I have been having a hard time finding a solution for this problem.
I am trying to set up EF Code First on an MVC4 Project. I have a User and Customer that both inherit from Person. I then have a Template object that has a Many-to-Many relationship with Customer and a One-to-Many relationship with User. Here is how I have it set up:
MODELS
public class Person
{
[Key]
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public string Email { get; set; }
public virtual List<Template> Templates { get; set; }
}
public class User : Person
{
....
}
public class Customer : Person
{
....
}
public class Template
{
public int TemplateId { get; set; }
public string TemplateName { get; set; }
public virtual List<Customer> Customers { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
}
CONTEXT
public class ProjectContext : DbContext
{
public ProjectContext()
: base("name=ProjectDB")
{
}
public DbSet<Template> Templates { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Template>()
.HasMany(x => x.Customers)
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);
}
}
If I remove the Person DBSet out of the context this works fine but sets up TPT inheritance. I would like to use TPH inheritance, but when I enable migrations with the Person DBSet in the context it chokes:
NavigationProperty 'Templates' is not valid. Type 'MvcProject.Models.Customer' of FromRole 'Template_Customers_Target' in AssociationType 'MvcProject.Models.Template_Customers' must exactly match with the type 'MvcProject.Models.Person' on which this NavigationProperty is declared on.
Where am I going wrong here?
You cannot inherit navigation properties from a base entity. They always must be declared in the class the other end of the relationship is refering to.
Template.Customers is refering to Customer (not to Person), hence the inverse navigation property Templates must be declared in Customer (not in Person)
Template.User is refering to User (not to Person), hence the inverse navigation property Templates must be declared in User (not in Person)
So, basically you must move the Templates collection from Person into both derived classes:
public class Person
{
// no Templates collection here
}
public class User : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
public class Customer : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
Then you can define the two relationships with Fluent API like so:
modelBuilder.Entity<Template>()
.HasMany(t => t.Customers)
.WithMany(c => c.Templates) // = Customer.Templates
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer"));
modelBuilder.Entity<Template>()
.HasRequired(t => t.User)
.WithMany(u => u.Templates) // = User.Templates
.HasForeignKey(t => t.UserId);
Change your HasMany selector to People:
modelBuilder.Entity<Template>()
.HasMany(x => x.People) // here
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);

Fluent NHibernate mapping using component

I have just started working on a project using Fluent NHibernate.
What is the correct way to map the following classes using Fluent NHibernate?
public class DurationUnit
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Duration
{
public virtual int Value { get; set; }
public virtual DurationUnit Unit { get; set; }
public virtual int DurationInMinutes { get{ throw new NotImplementedException(); } }
}
public class Event
{
public virtual int Id { get; set; }
public virtual String Name { get; set; }
public virtual Duration MaxDuration { get; set; }
public virtual Duration MinDuration { get; set; }
}
My inital approach was to declare a ClassMap for DurationUnit and Event, with Duration as a component of Event. When trying this I received an exception:
NHibernate.MappingException: Could not determine type for:
Entities.DurationUnit
if your mapping looks like this
public EventMap()
{
Component(x => x.MaxDuration, c =>
{
c.Map(x => x.Value, "MaxDurationValue");
c.Reference(x => x.Unit, "MaxDurationUnitId");
});
}
then make sure class DurationUnitMap is public and is added to the configuration