Code first relationships with entity framework, fluent API - asp.net-mvc

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());
}

Related

Add new entities to DbContext

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.

Remove primary key in entity framework table field in mvc4

I have created a model in the name of Sample with an Id and DocumentId property.In this I don't mention primary key for Id property.But it forms the primary key when I created the Sample as table in entity framework.I want to remove the primary key for Id. What do I have to do. Please help me. I am very new to mvc4.
public class Sample
{
[Required,DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
public int DocumentId { get; set; }
}
public override void Up()
{
CreateTable(
"dbo.Samples",
c => new
{
Id = c.Int(nullable: false, identity: false),
DocumentId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id);
}
I am putting this here as I don't think it will show up very well in comments :)
In the case of a user having many roles (and each role possibly being played by many users), you would have three tables linked to 2 classes. The tables would be a Users table with a unique UserId column and the rest of the user details. Another table would be Roles with a unique RoleId and the rest of the role information and a joining table that would have the id of the user and the id of the role he plays (this table could have a unique id of itself). If the user has 2 roles, they would have 2 records in the joining table, one for each of the roles. The classes would look something like this:
public class User{
public long UserId {get;set;}
public ICollection<Role> roles{get;set;}
//Other properties of the user name, DOb,etc.
}
public class Role{
public long RoleId{get;set;}
public ICollection<User> Users{get;set;}
//other properties of Role
}
This is a many to many relationship. Of course you could also have it as a one to many relationship if the role can be played by one user. In that case you don't need the joining table and you can just add a UserId column to the Role table and instead of a collection of users, the role would have a single property of type user (not really needed unless you want to navigate back from role to user).
Try adding the NotMapped attribute in your Id.
public class Sample
{
[Key]
public int SampleId {get;set;}
[Required,DatabaseGenerated(DatabaseGeneratedOption.None)]
[NotMapped]
public int Id { get; set; }
[Required]
public int DocumentId { get; set; }
}
EDIT:
I added a key attribute to specify your primary key.
You can also try this, which I think is better:
public class Sample
{
public int SampleId {get;set;}
public virtual ICollection<Document> Documents {get;set;}
}
public class Document
{
public int DocumentId {get;set;}
public int SampleId {get;set;}
public virtual Sample Sample {get;set;}
}
virtual keyword is for lazy loading.
Open the entity diagram (.edmx) file and find which ever property in that particular table is the primary key. Right click on it and select "properties". In the properties window see where it says StoreGeneratedPattern - Identity? Change that to none and save the diagram. It will regenerate the .cs model files by itself
Fluent API suppresses the Data Annotations in Entity framework. Data Annotation for Primary is [Key] and ID is by default Primary key with identity.
In that scenario, Delete(if it's there) Data Annotation of ID i:e;[KEY] and use Fluent API in your context class. In below example, my primary key is "CustomerName" due to Fluent. Example:
public class Customer
{
public int CustomerName { get; set; }
public int ID { get; set; }
[Required]
public string ProductID { get; set; }
}
In Context Class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().HasKey(s => s.CustomerName);
}

Establish Foreign Key Connection Using Entity Framework With SQL Queries

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.

EF 4 Code First: Define Navigation Property

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();

Entity Framework 4.1 Code First: Single column foreign-key to multiple entities

I've been trying to create model in EF 4.1 to represent a database schema with a single table and column holding foreign keys from two other tables, but have had little luck with both annotations and the fluent API. A sample model is shown here:
public class User
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class Account
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class ExtendedAttribute
{
public Guid Id {get; set;}
public Guid ItemId {get; set;} // both Account.Id and User.Id stored here
public string Value { get; set; }
}
Currently the configuration for these entities looks something like this for both User and Account modelBuilders:
this.HasOptional(u => u.ExtendedAttributes).WithRequired();
Any thoughts on how to do achieve? Many thanks.
It is even not possible with the database itself and EF will not put any abstraction for that. You must have separate column and navigation property for each entity.

Resources