I have created an entity class in my MVC 3 application. One of the attribute named RegistryId is primary key as well as foreign key. How can I make a column primary key as well as Foreign key ? I am not using EF ORM designer. I am coding classes by hand.
I think by "not using EF ORM designer" you mean new DbContext API from EF 4.1. Because if you don't mean DbContext API you still have to use EDMX (designer).
You can either use data annotations (System.ComponentModel.DataAnnotations): KeyAttribute and ForeignKeyAttribute:
public class Registry
{
public virtual int Id { get; set; }
public virtual MyEntity MyEntity { get; set; }
}
public class MyEntity
{
[Key, ForeignKey("Registry")]
public virtual int RegistryId { get; set; }
public virtual Registry Registry { get; set; }
}
Or you can use fluent API (overriding OnModelCreating in your derived context):
(Edit: fluent mapping was reversed and incomplete)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyEntity>()
.HasKey(e => e.RegistryId);
modelBuilder.Entity<MyEntity>()
.Property(e => e.RegistryId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<MyEntity>()
.HasRequired(e => e.Registry)
.WithRequiredDependent(r => r.MyEntity);
}
Where MyEntity is your entity with FK and Registry is principal entity in 1:1 relationship.
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.
This question is in regards to ASP.NET MVC 5.0 model class.
I have a ASP.NET MVC5.0 model class for example that looks like this
public class Car
{
public int ID { get; set; }
public string CarType { get; set; }
public string SelectedType { get; set; }
}
However, I do not want a database field created for this model for property SelectedType.
Is there any attribute available to tell the Entity Framework not to create a database field for SelectedType?
Thank you for your help in advance.
you can use Fluent API ignore for example;
modelBuilder.Entity<Department>().Ignore(t => t.Budget);
You can use the NotMapped Data Annotation to exclude a purticular property like below:
public class Car
{
//
[NotMapped]
public string SelectedType { set; get; }
}
You also can do this with Fluent API, but you need to override the OnModelCreating like below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>().Ignore(n => n.SelectedType );
base.OnModelCreating(modelBuilder);
}
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 am using an existing database with EF Code First and using the modelBuilder to configure. I have two tables where a SESSION can have a SUBJECT, classes are as such:
public class SessionItem {
[Key]
public int SessionId { get;set; }
// Other Values
public int Subject_ID { get;set; }
public virtual Subject Subject { get;set; }
}
public class SubjectItem {
[Key]
public int Subject_ID { get;set; }
// Other Values
public virtual SessionItem Session { get;set; }
}
And then the modelBuilder code is:
modelBuilder.Entity<SessionItem>().ToTable("tblTblSessions");
modelBuilder.Entity<Subject>().ToTable("tblTblSubjects");
modelBuilder.Entity<SessionItem>()
.HasOptional<Subject>(u => u.Subject)
.WithOptionalDependent(c => c.Session).Map(p => p.MapKey("Subject_ID"));
This failed at first until I removed Subject_ID from the SessionItem class, then I got the error: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
Any idea where I have gone wrong?
Unfortunely one-to-one foreign key associations are not supported with Entity Framework because EF doesn't know what a unique key constraint is (that your Subject_ID column in the Session table apparently has).
You must workaround this by mapping the relationship as one-to-many. Follow the mapping in #flem's answer for the SessionItem entity and for the SubjectItem entity either remove the public virtual SessionItem Session { get;set; } altogether or replace it by
public virtual ICollection<SessionItem> Sessions { get; set; }
You don't need the mapping with Fluent API anymore for this relationship, or if you want, it should be:
modelBuilder.Entity<SessionItem>()
.HasOptional(se => se.Subject)
.WithMany() // or WithMany(su => su.Sessions)
.HasForeignKey(se => se.Subject_ID);
When you add items to that collection you must ensure in your business logic that you don't add more than one item because you can't have more than one row with the same Subject_ID in your database due to the unique key constraint. When you load a subject from the database including the sessions the session collection is either empty or has one single element, but not more.
Try this:
public class SessionItem
{
[Key]
public int SessionId { get;set; }
// Other Values
[ForeignKey("Subject")]
public int? Subject_ID { get;set; }
[ForeignKey("Subject_ID")]
public virtual SubjectItem Subject { get;set; }
}
You need to make Subject_ID foreign key nullable.
[Table("tblTblSessions")]
public class SessionItem {
[Key]
public int SessionId { get; set; }
public virtual SubjectItem Subject { get; set; }
}
[Table("tblTblSubjects")]
public class SubjectItem {
[Key, ForeignKey("Session")]
public int Subject_ID { get; set; }
public virtual SessionItem Session { get; set; }
}
One-to-one relationship between SubjectItem and SessionItem, and you can get rid of all your modelBuilder code - all the table naming and one-to-one mapping you were doing is taken care of with the attributes and properties above.
Edit: Fixed a typo and marked the Dependent side of the one-to-one.
I have a couple of classes (for this example anyway) that use code first with the entity framework to connect to the database.
public class Customer
{
[Key]
public long CustomerId { get; set; }
public string CompanyName { get; set; }
...
public virtual List<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public long ContactId { get; set; }
public string Forename { get; set; }
...
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
When I hook these up in my context class directly to the db the foreign key relationships hook up fine and I can access the collection of contacts from within the customer class.
class RemoteServerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Contact> Contacts { get; set; }
...
}
My problem is that these database tables are used by various different systems and are massive. In order to increase efficiency I have overridden the default behaviour to point at a view (and also a stored proc elsewhere) rather than directly at the table.
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
public IEnumerable<Contact> Contacts()
{
return Database.SqlQuery<Contact>("SELECT * FROM vw_ContactsList");
}
I have made sure that in each of the views I have included the foreign key fields: CustomerId and ContactId.
When I do this however the class joins appear to be lost - there's always a null when I drill into either of the objects where it should be pointing to the other one. I have tried to set up what the foreign key field should point to but this doesn't seem to help either.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasRequired(p => p.Customer)
.WithMany()
.HasForeignKey(k => k.CustomerId);
}
Is there a way to establish the connection when overriding the default behaviour?
There is no overriding in this case. If you removed
public DbSet<Customer> Customers { get; set; }
and replaced it with
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
you have completely changed the behavior. The first uses entities and full power of EF. The second is only helper to execute custom SQL. Second without first or without defining entity in OnModelCreating doesn't use Customer as mapped entity at all - it uses it as any normal class (only mapped entities can use features like lazy loading).
Because your Customer is now mapped to view you cannot use your former Customer class used with table. You must define mapping of Customer to a view by cheating EF:
modelBuilder.Entity<Customer>().ToTable("vw_ContactsList"); // EF code fist has no view mapping
Once you have this you can try again using:
public DbSet<Customer> Customers { get; set; }
Unless your view is updatable you will get exception each time you try to add, update or delete any customer in this set. After mapping relation between Customer and Contact mapped to views your navigation properties should hopefully work.
The problem with SqlQuery is the way how it works. It returns detached entities. Detached entities are not connected to the context and they will not lazy load its navigation properties. You must manually attach each Customer instance back to context and to do that you again need DbSet.