I am trying to define a one to many relationship between Category and Project (a Category can have one or many Projects, a Project can have one or no Category)
public class Project : Entity {
public virtual string Title { get; set; }
public virtual Guid? CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Category : Entity {
public virtual string Name { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
I have defined the following mappings:
modelBuilder.Entity<Project>()
.MapSingleType(p => new {
ProjectId = p.Id,
p.CategoryId,
p.Title,
p.Slug,
p.ShortDescription,
p.Description,
p.CreatedOn,
p.UpdatedOn
})
.ToTable("Projects");
modelBuilder.Entity<Category>()
.MapSingleType(c => new {
CategoryId = c.Id,
c.Name,
c.CreatedOn,
c.UpdatedOn
})
.ToTable("Categories");
// relationships
modelBuilder.Entity<Project>()
.HasOptional<Category>(p => p.Category)
.WithMany()
.HasConstraint((p, c) => p.CategoryId == c.Id);
Now although this appears to be working fine, EF is still generating a Categories_Products table (used for many to many associations).
I've disabled the default database initializer yet this table is still being generated. What am I doing wrong?
Thanks
Ben
I removed the project and category mapping code and let EF use default conventions to create the database. This created the relationship I expected (one to many between category and project).
I would add that the only reason that I was explicitly defining the mapping was because EF does not appear to handle base classes very well. I had a base class "Entity" with a single property "Id" that all my entities inherited from. This caused so many problems with CTP4 that I just swapped it with an interface IEntity. This still gave me the constraints I needed when working with generic repository classes.
Hopefully entity base classes will be better supported in the RTM
Related
I use ASP.NET Core with Identity and want to extend default Db context. If I want to add not linked table I just add a new class:
public partial class Table1
{
public int Id { get; set; }
public string Txt { get; set; }
}
and extend my ApplicationDbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Table1> Table1 { get; set; }
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);
builder.Entity<Table1>(entity =>
{
entity.ToTable("Table_1");
entity.Property(e => e.Id).HasColumnName("ID");
entity.Property(e => e.Txt)
.IsRequired()
.HasMaxLength(50);
});
}
}
then create a migration and update db. It works. But if I want to add a new table, which linked to table from IdentityDbContext:
public partial class Users
{
public int Id { get; set; }
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual AspNetUser User { get; set; }
}
of course, AspNetUser class does not exist (it's created by IdentityDbContext, as I understand). How to do it correctly?
The class is most likely named ApplicationUser (the default). The table that represents this entity is dbo.AspNetUsers, but that is set by Identity, and has nothing to do with the class name.
FWIW, though, it's a bad idea to create a Users entity, for a number of reasons:
There will undoubtedly be confusion between Users and ApplicationUser, as well as the database tables dbo.Users and dbo.AspNetUsers.
In general, you should name your entities in singular tense, i.e. User, not Users. There's a whole host of reasons for this convention, but suffice to say, it just makes your code better and more readable to stick to singular tense for singular things and plural tense for plural things. For example, a property of type ICollection<User> would be named Users, since it's composed of many User instances.
What you're doing is completely unnecessary. The whole reason for Identity's existence is that Membership (the previous authentication and authorization framework employed by ASP.NET) did not allow you to extend the types involved. Identity changes all this and is 100% extensible in every way. You have full access to all entities involved in the the framework and you can add to them and derive from them. If you want to add additional properties for "users" in your system, just add them to the ApplicationUser class directly.
I have a problem, I have to create a model where we have two entities which CAN be linked together but can also exist as stand alone entities.
The model currently looks like this:
public class Authorisation
{
public int AuthorisationID { get; set; }
public virtual Change Change { get; set; }
}
public class Change
{
public int ChangeID{ get; set; }
public virtual Authorisation Authorisation { get; set; }
public int? AuthorisationID{ get; set; }
}
The reason for this is that we can have an authorization record independent of a change, and some changes require authorisation and some dont, so neither side of the relationship is required.
I can configure this with the fluent API like so:
modelBuilder.Entity<Authorisation>()
.HasOptional(t => t.Change)
.WithOptionalPrincipal(t => t.Authorisation);
And alls well, Except that the migration that it creates looks like this
CreateTable(
"dbo.Changes",
c => new
{
ChangeID = c.Int(nullable: false, identity: true),
AuthorisationID = c.Int(),
Authorisation_AuthorisationID = c.Int(),
})
.PrimaryKey(t => t.ChangeID)
.ForeignKey("dbo.Authorisations", t => t.Authorisation_AuthorisationID)
.Index(t => t.Authorisation_AuthorisationID);
EF is deciding that its going to add a new column (Authorisation_AuthorisationID) for me to use as the FK between the two entities, what I really want to be able to do is to use the change.AuthorisationID property as the FK onto the Authorisation, I cannot find a way to configure this at all (Please note that I need the FK to be in the model - for consistency with the rest of the app more than anything else).
To sum up I need to be able to create a relationship between two entities where both sides of the relationship are optional and if possible I want to be able to define the FK column myself.
Am I just approaching this wrong? Ive been staring at the same block of code for so long I could be missing something simple.
Looks like explicit foreign key property is not supported for one-to-one relationships - there is no HasForeignKey Fluent API and also if you put ForeignKey attribute on the navigation property you get exception saying that multiplicity must be *.
So the only choice you have is to remove the explicit Change.AuthorisationID property and work only with navigation properties:
Model:
public class Authorisation
{
public int AuthorisationID { get; set; }
public virtual Change Change { get; set; }
}
public class Change
{
public int ChangeID{ get; set; }
public virtual Authorisation Authorisation { get; set; }
}
Configuration:
modelBuilder.Entity<Authorisation>()
.HasOptional(t => t.Change)
.WithOptionalPrincipal(t => t.Authorisation)
.Map(a => a.MapKey("AuthorisationID"));
I need to create this db context:
public class Contact
{
public int ID { get; set; }
public virtual List<Contact> Employers { get; set; }
public virtual List<Contact> Staff { get; set; }
}
EF creates the table ContactContacs with columns Contact_ID and Contact_ID1. How (where) can I rename this context columns?
In your DbContext's OnModelCreating() method:
modelBuilder.Entity<Contact>().HasMany(x => x.Employers).WithMany().Map(x =>
{
x.ToTable("ContactEmployers");
x.MapLeftKey("ContactId");
x.MapRightKey("EmployerId");
});
modelBuilder.Entity<Contact>() => The model you're configuring
HasMany(x => x.Employers) => Indicates it's a many-to-X relationship with a navigation property
WithMany() => Configures it as a many-to-many relationship without a navigation property on the other side
Map() => Configuring the table and columns for the IDs
I have a legacy table I need to connect my app to. I am using a code-first, POCO model. I have the following classes:
public class Equipment
{
[Key]
public string EquipmentId { get; set; }
public string OriginatorId { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
[Key]
[Column("employee_id")]
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
[ForeignKey("OriginatorEmployeeId")]
public virtual Equipment Equipment { get; set; }
}
I need to map EmployeeId in the Employee class to to OriginatorEmployeeId in the Equipment class.
Also, the legacy table is represented by the Employee class. The table is actually named employee (lower case) and the EmployeeId column is named "employee_id". I want to keep naming of my classes and properties consistent with the rest of the app, hence Employee and EmployeeId.
Here is what I have tried using fluent API:
modelBuilder.Entity<Employee>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("employee");
});
modelBuilder.Entity<Equipment>()
.HasOptional<Employee>(u => u.Employee)
.WithOptionalDependent(c => c.Equipment).Map(p => p.MapKey("OriginatorEmployeeId"));
I am probably mixing things I don't need to. The error I am getting right now is:
Multiplicity is not valid in Role 'Equipment_Employee_Source' in relationship 'Equipment_Employee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Any help is appreciated.
Can an employee record be associated to more than one equipment record? If they can then your Employee POCO should contain a collection property representing a one-to-many relationship between Employee and Equipment.
public virtual ICollection<Equipment> Equipments {get;set;}
You configuration should then be adjusted accordingly to show this relationship:
modelBuilder.Entity<Employee>()
.HasMany<Equipment>(u => u.Equipments)
.WithRequired(c => c.Employee).HasForeignKey(p => p.OriginatorId);
It also looks like you will need to setup a configuration for your column name mappings as well. Therefore, I would recommend that you create a separate configuration file for each of your POCOs to make it easier to manage the configurations, then just add those configurations to the modelbuilder.Configurations collection in your OnModelCreating event of your DBContext
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelbuilder.Configurations.Add(new EmployeeConfiguration());
}
I have following entities:
public class Product {
[Key]
public int Id{get;set;}
//other properties
}
public Coupon {
[Key]
public int Id {get;set;}
//other properties
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<CouponCode> CouponCodes { get; set; }
}
I am configuring DbModelBuilder as follows:
var builder = new DbModelBuilder();
builder.Entity<Product>().HasKey(p => p.Id);
builder.Entity<Coupon>().HasKey(a => a.Id);
//other properties
builder.Entity<Coupon>().HasMany(x => x.CouponCodes);
builder.Entity<Coupon>().HasMany(x => x.Products);
This scheme is creating Coupon_Id in Products table. Actually I want to register all the product codes for which the given Coupun is valid. The way EF is interpreting it is obviously wrong as there an be multiple coupons valid for one Product entity.
Kindly help me find what I'm doing wrong.
If you want to have many-to-many relation you must instruct EF to create it.
builder.Entity<Coupon>().HasMany(x => x.Products).WithMany();