I am trying to implement two DbContexts which map to two seperate schemas/databases within MySql with a foreign key between them.
I understand questions similar to this have been asked before, but I can't find an answer in relation to MySql
I am using code first but and I'm getting the following error when I do Update-Database:
MultipleDbContext.ApplicationUser: : EntityType 'ApplicationUser' has no key defined. Define the key for this EntityType.
ApplicationUsers: EntityType: EntitySet 'ApplicationUsers' is based on type 'ApplicationUser' that has no keys defined.
These are my 2 DbContexts:
ApplicationDbContext
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext() : base("ApplicationDBContext") {}
public DbSet<Application> Applications { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ApplicationConfiguration());
}
}
public class ApplicationConfiguration : EntityTypeConfiguration<Application>
{
public ApplicationConfiguration()
{
HasKey(x => x.ApplicationID);
Property(x => x.ApplicationID).IsRequired();
Property(x => x.ApplicationName).IsRequired();
HasRequired(x => x.PrimaryUser).WithMany().HasForeignKey(x => x.UserID);
}
}
ApplicationUserDbContext
public class ApplicationUserDbContext : DbContext
{
public ApplicationUserDbContext() : base("UserDBContext") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationUserConfiguration());
}
}
public class ApplicationUserConfiguration : EntityTypeConfiguration<ApplicationUser>
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public ApplicationUserConfiguration()
{
HasKey(x => x.UserID);
Property(x => x.UserID).IsRequired();
Property(x => x.Name).IsRequired();
}
}
This is my update database statement:
update-database -ConfigurationTypeName MultipleDbContext.Migrations.Configuration
Thanks!
EDIT - Adding Entity Objects
public class Application
{
public int ApplicationID { get; set; }
public string ApplicationName { get; set; }
public int UserID { get; set; }
public virtual ApplicationUser PrimaryUser { get; set; }
}
public class ApplicationUser
{
public int UserID { get; set; }
public string Name { get; set; }
}
Problem probably is, that you have string property as a the primary key for your User and the length of this key is too long for MySql. I believe the limitation is 96 characters for keys (767 bytes).
A way to handle this would be to take only a subset of the string property and apply this to the key. To make the point clear, the following shows a key, which is only 4 chars long.
CREATE TABLE IF NOT EXISTS `foo` (
`id` varchar(128),
PRIMARY KEY (`id`(4)),
)
I'd suggest going all in with integer primary keys for the Aspnet.Identity stack - and also make tablenames all lowercase, since this would be an issue on mysql server hosted via case-sensitive filesystems.
This GitHub repo is kinda overkill as an example, but im pointing out a few points in the code here
Also this answer has a nice walkthrough
Related
I what have many-to-many relationship between two entities. Everything works fine. Is there a way to define names of FKs in intermediate table(StoresPushNotifications)?
The reason to ask is that mysql do not allow to define long constraint names. It generates random FK in case. It breaks migration when I try to set migration to an earlier step.
[Table("Stores")]
public class StoreEntity
{
[Key]
public int Id { get; set; }
public virtual ICollection<PushNotificationEntity> PushNotifications { get; set; }
}
[Table("PushNotifications")]
public class PushNotificationEntity
{
[Key]
public int Id { get; set; }
public ICollection<StoreEntity> Stores { get; set; }
}
In my Context file,
modelBuilder.Entity<StoreEntity>()
.HasMany<PushNotificationEntity>(s => s.PushNotifications)
.WithMany(c => c.Stores)
.Map(cs =>
{
cs.MapLeftKey("StoreId");
cs.MapRightKey("PushNotificationId");
cs.ToTable("StoresPushNotifications");
});
I had a similar problem and it was actually related to the migration key lengthrather than the foreign keys.
I resolved by modifying the migration configuration method to:
internal sealed class Configuration : DbMigrationsConfiguration<CCDatabase.CCDbContext>
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations";
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
}
and then adding this method to limit the key lengths
public class MySqlHistoryContext : HistoryContext
{
public MySqlHistoryContext(DbConnection connection, string defaultSchema) : base(connection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
}
}
I have the following Entities which I am persisting using EntityFramework CodeFirst:
public class User {
RedGroup RedGroup { get; protected set; }
virtual ICollection<GreenGroup> GreenGroups { get; }
int Id { get; protected set; }
int? RedGroupId { get; protected set; }
}
public abstract class Group {
int Id { get; protected set; }
virtual ICollection<User> Users { get; protected set; }
}
public class RedGroup : Group {
// Other properties
}
public class GreenGroup : Group {
// Other properties
}
Essentially, the user can belong to zero or one red groups, and more than one green group. Each group has a collection of users that belong to it.
I am trying to set up EF using CodeFirst with TPT and am having trouble sorting the mappings. At the moment, I have the following in OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new RedGroupMap());
modelBuilder.Configurations.Add(new GreenGroupMap());
modelBuilder.Configurations.Add(new UserMap());
}
These are the mapping classes:
public abstract class GroupMap<T> : EntityTypeConfiguration<T>
where T : Group {
public GroupMap() {
this.ToTable("Groups");
this.HasKey(t => t.Id);
this.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("Id");
// Also has other non-relationship mappings
}
}
public class RedGroupMap() : GroupMap<RedGroup> {
public RedGroupMap() {
this.ToTable("RedGroups");
// Also has other non-relationship mappings
}
}
public class GreenGroupMap() : GroupMap<GreenGroup> {
public GreenGroupMap() {
this.ToTable("GreenGroups");
this.HasMany(c => c.Users)
.WithMany(p => p.GreenGroups)
.Map(m =>
{
m.MapLeftKey("GreenGroupId");
m.MapRightKey("UserId");
m.ToTable("Users_GreenGroups");
});
// Also has other non-relationship mappings
}
}
public class UserMap() : EntityTypeConfiguration<User> {
this.ToTable("Users");
this.HasKey(t => t.Id);
this.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("Id");
this.HasOptional(t => t.RedGroup)
.WithMany(t => t.Users)
.Map(x => x.MapKey("RedGroupId"))
.WillCascadeOnDelete(false);
}
I am getting the following runtime error:
Users: FromRole: NavigationProperty 'Users' is not valid. Type 'RedGroup' of FromRole 'User_RedGroup_Target' in AssociationType 'User_RedGroup' must exactly match with the type 'GreenGroup' on which this NavigationProperty is declared on.
Afraid I'm stumped on how to set up this.
How can I set up the EntityFramework mappings to allow a Table per Type hierarchy?
I created a context without your mappings, but with a much simpler configuration and everything appeared to create OK:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Group>().ToTable("Groups");
modelBuilder.Entity<RedGroup>().ToTable("RedGroups");
modelBuilder.Entity<GreenGroup>().ToTable("GreenGroups");
}
I've noticed that you've defined [User].HasOptional(t => t.RedGroup), but the RedGroupId field on User is defined as int and not int? - perhaps this is related?
public class User {
int? RedGroupId { get; protected set; } // int -> int?
RedGroup RedGroup { get; protected set; } // virtual missing, but shouldn't matter
// Other properties
}
If RedGroup is required, try using .HasRequired instead.
I have an entity Group which has a many to many relationship with a view VesselInfo. In database, the relationship is stored in a table, GroupVessel.
public class Group
{
public Group()
{
GroupVessels = new List<GroupVessel>();
}
public int GroupId { get; set; }
public virtual ICollection<GroupVessel> GroupVessels { get; set; }
}
public class GroupVessel
{
public int GroupVesselId { get; set; }
public int GroupId { get; set; }
public virtual Group Group { get; set; }
public int VesselId { get; set; }
public virtual VesselView Vessel { get; set; }
}
public class VesselView
{
public int VesselId { get; set; }
}
The entities are mapped in the context like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new VesselViewMapping());
modelBuilder.Configurations.Add(new GroupMapping());
modelBuilder.Configurations.Add(new GroupVesselMapping());
base.OnModelCreating(modelBuilder);
}
public class VesselViewMapping : EntityTypeConfiguration<VesselView>
{
public VesselViewMapping()
{
// Primary Key
HasKey(t => t.VesselId);
// Properties
Property(t => t.VesselId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
ToTable("v_VesselInfo");
Property(t => t.VesselId).HasColumnName("VesselId");
}
}
public class GroupMapping : EntityTypeConfiguration<Group>
{
public GroupMapping()
{
// Primary Key
HasKey(group => group.GroupId);
// Properties
// Table & Column Mappings
ToTable("Group");
Property(t => t.GroupId)
.HasColumnName("GroupId")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class GroupVesselMapping : EntityTypeConfiguration<GroupVessel>
{
public GroupVesselMapping()
{
// Primary Key
HasKey(group => group.GroupVesselId);
// Properties
// Table & Column Mappings
ToTable("GroupVessel");
Property(t => t.GroupVesselId)
.HasColumnName("GroupVesselId")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.VesselId).HasColumnName("VesselId").IsRequired();
Property(t => t.GroupId).HasColumnName("GroupId");
// Relationships
HasRequired(t => t.Group).WithMany(g => g.GroupVessels)
.HasForeignKey(t => t.GroupVesselId);
}
}
When I try to instantiate the context, I get the following error:
Exception Details: System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'GroupVessel_Group_Source' in relationship 'GroupVessel_Group'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
Some additional info:
In database, the GroupVessel.VesselId column is a foreign key to a table Vessels, which is the underlying table of the v_VesselInfo view. There is no navigation property from VesselView back to GroupVessel as there is no need to traverse the graph that way in the application.
Finally figured it out, I used the wrong field as the foreign key field, changed to GroupId and it works
I'm trying to use table per hierarchy (TPH) with two classes inheriting from an abstract base:
public abstract class StatusLog : EntityBase
{
public StatusLog()
: base()
{
StatusChangeValidations = new List<StatusChangeValidation>();
}
public virtual List<StatusChangeValidation> StatusChangeValidations { get; set; }
public int StatusId { get; set; }
}
public class DisputeStatusLog : StatusLog
{
public virtual Dispute Dispute { get; set; }
}
public class StatementStatusLog : StatusLog
{
public virtual Statement Statement { get; set; }
}
I've mapped them like this:
public class StatusLogMap : EntityTypeConfiguration<StatusLog>
{
public StatusLogMap()
{
// Primary Key
this.HasKey(t => t.Id);
...
}
}
public class DisputeStatusLogMap : EntityTypeConfiguration<DisputeStatusLog>
{
public DisputeStatusLogMap()
{
// Relationships
this.HasRequired(t => t.Dispute)
.WithMany(t => t.StatusLogs)
.Map(m => m.MapKey("ObjectInstanceID"));
}
}
public class StatementStatusLogMap : EntityTypeConfiguration<StatementStatusLog>
{
public StatementStatusLogMap()
{
// Relationships
this.HasRequired(t => t.Statement)
.WithMany(t => t.StatusLogs)
.Map(m => m.MapKey("ObjectInstanceID"));
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StatusLog>()
.Map<DisputeStatusLog>(e => e.Requires("ObjectID").HasValue("28"))
.Map<StatementStatusLog>(e => e.Requires("ObjectID").HasValue("111"));
}
I'd expected this to work, but it causes a validation exception immediately:
ObjectInstanceID: Name: Each property name in a type must be unique. Property name 'ObjectInstanceID' is already defined.
Adding a foreign key property for ObjectInstanceID and changing the mappings to use HasForeignKey instead of MapKey results in a slightly different error:
ObjectInstanceID: : There is no property with name 'ObjectInstanceID' defined in the type referred to by Role 'StatusLog'.
Is there something wrong with my mappings, or is this a scenario that Entity Framework doesn't support yet? I'm currently using the 9/23 nightly build of EF 6.01 as I'd read on the EF CodePlex site that it may have fixed this problem, but I had the exact same error with EF 6 RC1.
I know I can fix this by splitting these classes up into multiple tables and using TPT, but would really prefer to avoid this as it's an existing database.
I am using Nhibernate 3.2, along with a build of FluentNhibernate compatible with NH 3.2 and I have come to map a legacy part of my system. I believe it is possible to do what I require, but need some assistance in mapping it correctly.
I have a User Class below, with a list of Applications.
public class User
{
public virtual string UserID { get; set; }
public virtual int Id { get; set; }
public virtual IList<Application> Applications { get; set; }
}
I also have a Application class which has a foreign key back to the User Class which is not the "primary key". See Below
public class Application
{
// Not sure if this 'User' is needed?
public virtual User User { get; set; }
public virtual int IdFromUserTable { get; set; }
public virtual string ApplicationName { get; set; }
}
I have the corresponding ClassMaps, I think the UserMap is where the issue lies
UserMap
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserID);
HasMany(x => x.Applications)
.AsBag()
.KeyColumn("UserID")
.PropertyRef("Id")
.LazyLoad()
.Inverse();
Table("usertable");
ReadOnly();
}
}
ApplicationMap
public class ApplicationMap : ClassMap<Application>
{
public ApplicationMap()
{
CompositeId()
.KeyProperty(x => x.ApplicationName)
.KeyProperty(x => x.IdFromUserTable, "UserID");
Table("applicationstable");
}
}
The Table structures are as follows:
User Table
Primary Key - string, ColumnName = UserID
int (Identity) - int, ColumnName = Id
Applications Table
Composite Key - string, ColumnName = ApplicationName
and
int, ColumnName = UserId (references Id column in user table)
My question is how to get the mapping to work for the above scenario.
One caveat is: I cannot change the structure of the database, but I can change the classes / classmaps
Any help greatly appreciated..
PS - I did get this to work by adding in Fetch.Join in the HasMany userMap, but I would prefer to lazily evaluate the Application List when needed
Thanks, Mark