EF Code First creating useless columns in my database. Why? - asp.net-mvc

I have something like this:
public class Account
{
[Key]
public Guid AccountId { get; set; }
}
public class TransactionHistory
{
[Key]
public int Id { get; set; }
public Account Sender { get; set; }
public Account Receiver { get; set; }
}
My modelbuilder looks like this:
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Sender)
.WithMany()
.Map(s => s.MapKey("Sender"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Receiver)
.WithMany()
.Map(s => s.MapKey("Receiver"))
.WillCascadeOnDelete(false);
And in my database, my TransactionHistory table got three columns: sender, receiver, and Account_AccountId. While first two columns are ok, i don't want that third column and i don't know why CodeFirst created it... Can you help me with this?

A collection like ICollection<TransactionHistory> in Account will lead to a third relationship with its own foreign key in Account. That's the additional key you are seeing. If you don't want a third relationship you must decide which navigation property in Account you want to relate the collection to. Either...
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Sender)
.WithMany(account => account.TransactionHistories)
.Map(s => s.MapKey("Sender"))
.WillCascadeOnDelete(false);
...or...
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Receiver)
.WithMany(account => account.TransactionHistories)
.Map(s => s.MapKey("Receiver"))
.WillCascadeOnDelete(false);
But not both. Or alternatively remove the collection property and the third FK will disappear.

I think doing this will fix it.
[Column("ID")]
[Key]
public int Id { get; set; }
Also I would consider changing your mappings to s.MapKey("SenderAccountID") and s.MapKey("ReceiverAccountID"). Makes life easier for people working in your DB.

Related

How to rename db columns of linking table (many to many realiton) in ASP .NET MVC EF (code first)

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

How Entity framework works with lambda-expressions in ASP.NET MVC

I'm working with EF code first. There are two classes which define my many to many association tables:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string Email { get; set; }
public virtual ICollection<Habit> Habits { get; set; }
}
public class Habit
{
[Key]
public int HabitId { get; set; }
public virtual ICollection<UserProfile> Users { get; set; }
}
I need to select from my database all habits for current user. I'm very new to c#, and the problem is that I can't understand how to work with complex lambda-expressions. I tried that:
context.Habits.Where(habit => habit.Users
.Where(user=>user.Email==User.Identity.Name)).ToList()
But it's wrong. Could you please correct my lambda-based query. Thanks.
Why not add a DbSet<UserProfile> to your context and then do:
context.Users.Include("Habits")
.First(user => user.Email == User.Identity.Name)
.Habits;
If you want to avoid the above and rather fix the query you should do:
context.Habits.Where(habit =>
habit.Users.Any(user=>user.Email==User.Identity.Name)).ToList();
Any returns true if any item in the IEnumerable satisfies the condition.
Try using Select (or SelectMany if you want a single flattened list) for the first lambda expression:
context.Habits.Select(habit =>
habit.Users.Where(user => user.Email == User.Identity.Name)
).ToList()
The problem is that Where requires that the lambda expression return a boolean, and it was returning an IEnumerable<UserProfile>.
Because you want to select a specific property (Habits) from the UserProfile entity the most straight-forward way would be to use Select in my opinion:
var habits = context.UserProfiles
.Where(user => user.Email == User.Identity.Name)
.Select(user => user.Habits)
.SingleOrDefault(); // I assume here that the Email is unique

EntityFramework DataAnnotations validation doesnt work

I've used EF Power Tools to auto generate my models from an existing database. Im using DataAnnotations to perform required validation, which works for most part except when validating properties that have foreign key relationship with other tables (one to many, etc..). What do I need to do in order to accomplish the validation of these properties?
In the below code, I'm trying to make DistributorId property a required field.
public class Event
{
public Event()
public int EventId { get; set; }
[Remote ("CheckDuplicateEventName","Event",AdditionalFields="InsertMode")]
[Required(ErrorMessage = "Event Name is required.")]
public string Name { get; set; }
[Required(ErrorMessage = "Distributor is required.")]
public int DistributorId { get; set; }
public virtual Distributor Distributor { get; set; }
}
Mapping class
public EventMap()
{
// Primary Key
this.HasKey(t => t.EventId);
// Properties
this.Property(t => t.Name)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
this.ToTable("Events");
this.Property(t => t.EventId).HasColumnName("EventId");
this.Property(t => t.Name).HasColumnName("Name");
this.Property(t => t.DistributorId).HasColumnName("DistributorId");
// Relationships
this.HasRequired(t => t.Distributor)
.WithMany(t => t.Events)
.HasForeignKey(d => d.DistributorId);
}
Tnx!
This is very simply because Name is a string (which is nullable), while DistributorId is an int (not nullable). That means that DistributorId always exists (although can be not the value you looking for, but still [Required] validation is satisfied.
What you probably want to change is to
either replace [Required] with something that will validate the actual value [Range] is great example here.
or have DistributorId as string converting it to int before writing to the database.
Hope this helps

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

Code First Mapping for Entity Framework Hierarchy

I have a model that looks like this:
public class Category
{
public string Id { get; set; }
public string Description { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public ICollection<Product> Products { get; set; }
}
With a database table that looks like
Categories
Id (PK varchar(5))
Description (nvarchar(50))
ParentId (FK varchar(5))
But Im stumped when it comes to setting up the mapping
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithMany(x => x.Children)
.Map(m =>
{
m.ToTable("Categories");
m.MapLeftKey(x => x.Id, "Id");
m.MapRightKey(x => x.Id, "ParentId");
});
I can see why the mapping fails (StackOverflowException), but am unsure as to how to fix it. Any help would be greately appreciated.
This is using the latest release of EF (4.1?).
Thanks!
Why do you map many-to-many relation on the same navigation property? That is completely wrong. First your table obviously expect one-to-many relation. Even if you need many-to-many relation you cannot use the same navigation property for that.
Just try:
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithOptional(y => y.Parent)
.Map(m => m.MapKey("ParentId"));

Resources