Manually map 1 to Many Relationship - entity-framework

I am trying to map an existing database in EF with code first. The provider (jetEntityFrameworkProvider) does not support DB first.
I am trying to map the Table "Component" (1) to the Table "ComponentText" (Many)
This is what I have
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().Map(m =>
{
m.Properties(p => new { p.ComponentText });
m.ToTable("ComponentText");
});
modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
}
When I run it I get the follow error
System.InvalidOperationException: 'The property 'ComponentText' on type 'Component' cannot be mapped because it has been explicitly excluded from the model or it is of a type not supported by the DbModelBuilderVersion being used.'
These are my models with only the relevant properties
Component
[Table("Component")]
public class Component
{
[Key]
[Column("Counter")]
public int Id { get; set; }
[Column("Name")]
public virtual ICollection<ComponentText> ComponentText { get; set; }
}
ComponentText
[Table("ComponentText")]
public class ComponentText
{
[Key]
[Column("Counter")]
public int Id { get; set; }
public int TextId { get; set; }
public string Text { get; set; }
//** Foreign Key
public int ComponentCounter { get; set; }
}
ETA:
I've changed my code per Backs answer. However, it is still not working. I have tried several variations. .HasRequired(), .HasOptional().
Note I removed m.ToTable("ComponentText"); As Component is already mapped in the class to the "Component" Table.
I am getting 0 results and receiving this error in Results View
Error = The function evaluation requires all threads to run.
If I uncomment the section line in the comment modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
I get this error
Component_ComponentText_Target: : Multiplicity is not valid in Role 'Component_ComponentText_Target' in relationship 'Component_ComponentText'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
public class ProjectContext : DbContext
{
private DbConnection con = new JetConnection();
public ProjectContext() : base(new JetConnection(#"Provider=Microsoft.Jet.OLEDB.4.0; Data Source = 'C:\Users\Ben-Laptop\Desktop\Test-Project.sep'; User Id = Admin; Jet OLEDB:Database Password = SEEME;"), true)
{
Database.SetInitializer<ProjectContext>(null);
}
public DbSet<Component> Components { get; set; }
public DbSet<Content> Contents { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().HasMany(p => p.ComponentText).WithOptional().HasForeignKey(p => p.ComponentCounter);
//modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
}
}

Remove m.Properties(p => new { p.ComponentText }); because it only maps property ComponentText
Add mapping for collection
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().Map(m =>
{
m.ToTable("ComponentText");
})
.HasMany(p => p.ComponentText)
.WithRequired()
.HasForeignKey(p => p.ComponentCounter);
}
Entity Framework Fluent API - Relationships

Related

How to create a bridging table between DbContext and IdentityDbContext?

I want to create a bridging table between dbo.AspNetUsers and dbo.Recipe called dbo.Binder. A user can save many recipes and a recipe can be saved by many users (many-to-many).
I'm able to create bridging tables within my DbContext fine, called RecipeContext, e.g dbo.Recipe, dbo.TagRecipe and dbo.Tag, but I am confused how I'm suppose to do this between two different DbContexts, that being RecipeContext (DbContext) and ApplicationDbContext(IdentityDbContext<IdentityUser>).
One solution I read was for RecipeContext to inherit from IdentityDbContext<IdentityUser>, like so:
public class RecipeContext :IdentityDbContext<IdentityUser>
Though when I do this and run
Update-Database -Context RecipeContext
I get an error
There is already an object named `AspNetRoles` in the database
I suppose this is from when I had the contexts separate, and yes, the AspNetRoles/identity tables are already created in my database from before.
I'm wondering if for one, I'm on the right track and two, what do I do about the fact the Identity tables already exist?
Thank you
For reference
RecipeContext:
public class RecipeContext :IdentityDbContext<IdentityUser> /*: DbContext*/
{
public RecipeContext(DbContextOptions options) : base(options) { }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<TagRecipe> TagRecipes { get; set; }
public DbSet<StarRating> StarRatings { get; set; }
public DbSet<Binder> Binders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Identity
base.OnModelCreating(modelBuilder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
...(code left out)
// Override pural table names to singular
modelBuilder.Entity<Binder>().ToTable("Binder");
// Declare use UserId and RecipeId as primary keys for Binder
modelBuilder.Entity<Binder>()
.HasKey(b => new { b.UserId, b.RecipeId });
modelBuilder.Entity<Binder>()
.HasOne<ApplicationUser>(b => b.User)
.WithMany(u => u.Binders)
.HasForeignKey(b => b.UserId);
modelBuilder.Entity<Binder>()
.HasOne<Recipe>(b => b.Recipe)
.WithMany(u => u.Binders)
.HasForeignKey(b => b.RecipeId);
}
}
ApplicationDbContext (this is to be merged into RecipeContext):
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Binder class:
public class Binder
{
public string UserId { get; set; }
public int RecipeId { get; set; }
public virtual ApplicationUser User { get; set; }
public Recipe Recipe { get; set; }
}
ApplicationUser class (this was self created to create a collection of Binders)
public class ApplicationUser : IdentityUser
{
public ICollection<Binder> Binders { get; set; }
}
Recipe class:
public class Recipe
{
public int Id { get; set; }
...(code left out)
public ICollection<Binder> Binders { get; set; }
}
Startup class - ConfigureServices method (I feel some adjustments will need to be made if the two dbContexts are merging)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Identity
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddRazorPages(); // Added - Identity scaffhold Views
// Recipe context
services.AddDbContext<RecipeContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
}
appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=WCIMDBCoreEF1ModelTest;Trusted_Connection=True;MultipleActiveResultSets=true",
"ApplicationDbContextConnection": "Server=(localdb)\\mssqllocaldb;Database=MVCApp;Trusted_Connection=True;MultipleActiveResultSets=true"
}

Including derived child properties in Entity Framework Core 2.0

Using Entity Framework Core 2.0, I am trying to construct a query to include related data for a polymorphic child entity.
For example, given the following types:
public class ParentEntity
{
public int Id { get; set; }
public IList<ChildEntityBase> Children { get; set; }
}
public abstract class ChildEntityBase
{
public int Id { get; set; }
}
public class ChildEntityA : ChildEntityBase
{
}
public class ChildEntityB : ChildEntityBase
{
public IList<GrandchildEntity> Children { get; set; }
}
public class GrandchildEntity
{
public int Id { get; set; }
}
and the following configuration:
public DbSet<ParentEntity> ParentEntities { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ParentEntity>().HasKey(p => p.Id);
builder.Entity<ParentEntity>().HasMany(p => p.Children).WithOne();
builder.Entity<ChildEntityBase>().HasKey(c => c.Id);
builder.Entity<ChildEntityBase>()
.HasDiscriminator<string>("ChildEntityType")
.HasValue<ChildEntityA>("a")
.HasValue<ChildEntityB>("b");
builder.Entity<ChildEntityA>()
.HasBaseType<ChildEntityBase>();
builder.Entity<ChildEntityB>()
.HasBaseType<ChildEntityBase>()
.HasMany(u => u.Children).WithOne();
builder.Entity<GrandchildEntity>()
.HasBaseType<ChildEntityBase>();
base.OnModelCreating(builder);
}
I am trying to write the following query:
var result = this.serviceDbContext.ParentEntities
.Include(p => p.Children)
.ThenInclude((ChildEntityB b) => b.Children);
Unfortunately, this is resulting in a syntax error.
However, I believe I am following the syntax as specified in https://github.com/aspnet/EntityFrameworkCore/commit/07afd7aa330da5b6d90d518da7375d8bbf676dfd
Can anyone suggest what I'm doing wrong?
Thanks
This functionality is not available in EFC 2.0.
It's been tracked as #3910 Query: Support Include/ThenInclude for navigation on derived type and according to the current EFC Roadmap, it's scheduled for EFC 2.1 release (Include for derived types item under
Features we have committed to complete).

IdentityUserLogin<string>' requires a primary key to be defined error while adding migration [duplicate]

This question already has answers here:
The entity type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>' requires a key to be defined
(4 answers)
Closed 4 years ago.
I am using 2 different Dbcontexts. i want to use 2 different databases users and mycontext. While doing that i am getting a error The entity type 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin' requires a primary key to be defined. I think there is something wrong with IdentityUser please suggest me where can i change my code so that i can add migration.
My Dbcontext class:
class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public AppUser User {get; set;}
public string Content { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
and AppUser class:
public class AppUser : IdentityUser
{
//some other propeties
}
when I try to Add migration the following error occurs.
The entity type 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>' requires a primary key to be defined.
give me solution to resolve the issue..
To reduce the link to a nutshell, try this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
...
See Above link for more.
This issue will start coming as soon as you wrote the following lines in DBContext without adding 'base.OnModelCreating(modelBuilder);'
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
Two solutions:
1) Don't override OnModelCreating in DbContext Until it becomes necessary
2) Override but call base.OnModelCreating(modelBuilder)
The problem is AppUser is inherited from IdentityUser and their primary keys are not mapped in the method OnModelCreating of dbcontext.
There is already a post available with resolution. Visit the below link
EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType
Hope this helps.

Entity Framework and Data binding

I have two tables that are built using codefirst entity framework.
public class TimeEntry:Entity
{
[Required]
[Display(Name = "Activity")]
public int ActivityId { get; set; }
public virtual Activity Activity { get; set; }
}
public class Activity:Entity
{
private ICollection<TimeEntry> _timeEntries;
[Required]
public string Description { get; set; }
public virtual ICollection<TimeEntry> TimeEntries
{
get
{
return _timeEntries ?? (_timeEntries = new List<TimeEntry>());
}
set
{
_timeEntries = value;
}
}
}
public class Entity
{
public int Id { get; set; }
}
These are the classes I have created for my Db. There is no problem with creating the database. When I try to perform CRUD operations I get the error
DataBinding: 'System.Data.Entity.DynamicProxies.Activity_AD12BF558F098271F1F51B3B1489B4B3B281FD0B686C8457333DE5BEE0E8B6A9' does not contain a property with the name 'ActivityId'
It is trying to find ActivityId in the Activity table however the primary key is Id. How do I map the foreign key ActivityId in the TimeEntry table to the primary key Id in the Activity table.
You can use fluent api to let EF know about you mappings.
public class ActivityMap : EntityTypeConfiguration<Activity>
{
public ActivityMap()
{
this.HasKey(a => a.Id);
}
}
public class TimeEntryMap : EntityTypeConfiguration<TimeEntry>
{
public TimeEntryMap()
{
this.HasKey(t => t.Id);
// Relationships
this.HasRequired(t => t.Activity)
.WithMany(t => t.TimeEntries)
} .HasForeignKey(d => d.ActivityId);
}
Then in your context:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ActivityMap());
modelBuilder.Configurations.Add(new TimeEntryMap());
}
}
I think this will solve your issue.
Also, (as a side note) instead of defining _timeEntries, you can use auto implemented property for TimeEntries and initialize it in you ctor. like below:
public class Activity:Entity
{
public virtual ICollection<TimeEntry> TimeEntries { get; set; }
public Activity()
{
this.TimeEntries = new List<TimeEntry>();
}
}
hi i have the same problem
If one specifies DataKeyNames property as ID and the actual column name is CustomerID. It will throw the above error.
If one specifies DataTextField or DataValueField property as ID and the actual column name is CustomerID. It will throw the above error.
and found the answer here it work for me link
If you are using Code First, you need to indicate the mapping of ActivityId => Id by overriding OnModelCreating in your DbContext.
At a suggestion, it seems you are mixing the concerns of DTO and MVC ViewModel in the same entity. Why not separate these concerns into 2 different entities?

in entity framework code first, how to use KeyAttribute on multiple columns

I'm creating a POCO model to use with entity framework code first CTP5. I'm using the decoration to make a property map to a PK column. But how can I define a PK on more then one column, and specifically, how can I control order of the columns in the index? Is it a result of the order of properties in the class?
Thanks!
NOTE:
As of 2019 this answer became non-valid for later EntityFramework versions.
You can specify the column order in the attributes, for instance:
public class MyEntity
{
[Key, Column(Order=0)]
public int MyFirstKeyProperty { get; set; }
[Key, Column(Order=1)]
public int MySecondKeyProperty { get; set; }
[Key, Column(Order=2)]
public string MyThirdKeyProperty { get; set; }
// other properties
}
If you are using the Find method of a DbSet you must take this order for the key parameters into account.
To complete the correct answer submitted by Slauma, you can use the HasKey method to specify an order for composite primary keys as well:
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
public class Ctp5Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasKey(u => new
{
u.UserId,
u.Username
});
}
}
If, like me, you prefer to use a configuration file you can do that in this way (based on Manavi's example):
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("Users");
HasKey(x => new {x.UserId, x.Username});
}
}
Obviously you have to add the configuration file to your context:
public class Ctp5Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
}
}
Use as a anonymous object:
modelBuilder.Entity<UserExamAttemptQuestion>().ToTable("Users").HasKey(o => new { o.UserId, o.Username });