I'll try create one-to-one relation using EF and Fluent API.
First class:
public class Game
{
public int Id { get; set; }
public Guid Token { get; set; }
public string Player { get; set; }
public virtual Field Field { get; set; }
public virtual ICollection<Move> Moves { get; set; }
public GameStatus Status { get; set; }
public DateTime StartTime { get; set; }
public DateTime? EndTime { get; set; }
public PlayerCode Winner { get; set; }
public Game()
{
Status = GameStatus.NoteDone;
StartTime = DateTime.UtcNow;
Winner = PlayerCode.None;
Field = new Field {Game = this};
Token = Guid.NewGuid();
}
}
Secong class:
public class Field : IEntity
{
public int Id { get; set; }
public virtual Game Game { get; set; }
public string CellsString { get; set; }
}
And configure relations in context
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Game>()
.HasRequired<Field>(g => g.Field)
.WithRequiredDependent(f => f.Game);
base.OnModelCreating(modelBuilder);
}
But after this relation in DB is not created. Tables look like this
I try many variations of Fluent configuration, but no one works for me. Where i do mistake?
You can specify a mapping for foreign key if you don't wish to add it as a property to your entity class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Game>()
.HasRequired(g => g.Field)
.WithRequiredPrincipal(f => f.Game)
.Map(m => m.MapKey("GameId"));
}
You probably meant WithRequiredPrincipal, not WithRequiredDependent since you probably want that foreign key to be in the Field table.
Related
My model has Owners and Complexes. An owner can have many complexes, and a complex could theoretically have multiple owners (joint ownership). I want to be able to create new complexes and owners independently, so neither should require the other. However, when I try to add a new complex, I get this error:
Violation of PRIMARY KEY constraint 'PK_dbo.Owners'. Cannot insert duplicate key in object 'dbo.Owners'. The duplicate key value is (fcd72b09-b1ef-4894-83de-cb4897c0c401).
The statement has been terminated.
For the record, there is currently one existing owner (with the ID mentioned in the error). The owner is already associated with another complex. I should be able to add a new complex with this owner, but obviously it's not allowing me to.
What do I need to change with my model to accomodate this? Relevant code follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//modelBuilder.Entity<Complex>().ToTable("Complex");
//modelBuilder.Entity<Unit>().ToTable("Unit");
//modelBuilder.Entity<Address>().ToTable("Addresses");
//modelBuilder.Entity<Tenant>().ToTable("Tenant");
modelBuilder.Entity<ContactInfo>().ToTable("Contacts");
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Entity<Complex>()
.HasOptional(x => x.Owner)
.WithMany(x => x.Complexes);
modelBuilder.Entity<Unit>()
.HasOptional(x => x.Complex)
.WithMany(x => x.Units);
modelBuilder.Entity<Owner>()
.HasMany(x => x.Complexes);
base.OnModelCreating(modelBuilder);
}
}
Owner and Complex models:
public class Owner
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? ContactInfoId { get; set; }
[ForeignKey("ContactInfoId")]
public ContactInfo ContactInfo { get; set; }
public ICollection<StaffMember> Employees { get; set; }
public ICollection<Complex> Complexes { get; set; }
public Owner()
{
this.Id = System.Guid.NewGuid();
this.Employees = new HashSet<StaffMember>();
this.Complexes = new HashSet<Complex>();
}
public void AddEmployee(StaffMember employee)
{
Employees.Add(employee);
}
public void AddComplex(Complex complex)
{
Complexes.Add(complex);
}
}
public class Complex
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? OwnerId { get; set; }
[ForeignKey("OwnerId")]
public Owner Owner { get; set; }
public Guid? AddressId { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public virtual ICollection<Unit> Units { get; set; }
public virtual ICollection<StaffMember> StaffMembers { get; set; }
public Complex()
{
this.Id = System.Guid.NewGuid();
this.Units = new HashSet<Unit>();
this.StaffMembers = new HashSet<StaffMember>();
}
public void AddUnit(Unit unit)
{
Units.Add(unit);
}
public void AddStaff(StaffMember staffMember)
{
StaffMembers.Add(staffMember);
}
}
Your entities aren't setup correctly. In your Complex object, you are stating that it has only 1 owner so you're setting it up as a one to many instead of a many to many. If you set it as a collection instead of an object, EF will handle the many to many table for you
In EF i need to describe the following relationship:
a company may have many Locations, like
headquarter <= main location
plant ----+
warehouse |
store-1 +----> child Locations
store-2 |
store-n ----+
So I need a mainLocationID in the Location model, so that I can
1) given the main location I can access all its child locations
2) given a child location I can find its main location.
So I tried to do the following:
public class Location
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
public bool flagMainLocation { get; set; }
public int? mainLocationID { get; set; }
public virtual ICollection<Location> ChildLocations { get; set; }
}
and in my dbcontext
public class myappContext : DbContext
{
public myappContext() : base("myappContext")
{
}
public DbSet<Location> Locations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Location>()
.HasOptional(l => l.ChildLocations)
.WithMany()
.HasForeignKey(l => l.mainLocationID);
}
}
Now I'm stuck because as I try to scaffold a controller for the Location class I get the following error
"myapp.DAL.Location_ChildLocations:: Multiplicity conflicts with
the referential constraint in Role 'Location_ChildLocations_Target'
in relationship 'Location_ChildLocations'. Because all of the
properties in the Dependant Role are non-nullable, multiplicity
of the Principal Role must be '1'."
I'm not expert enough with EF to decrypt this error message.
Is there anyone who can tell me what is wrong with this configuration?
I would also like to be able to get the main location in this way
Location myChildLocation = db.Locations.Find(some_location_id);
Location mainLocation = myChildLocation.mainLocation;
What if you try this:
modelBuilder.Entity<Location>()
.HasMany(l => l.ChildLocations)
.WithOptional()
.HasForeignKey(l => l.mainLocationID);
Ok, with the hint from BiffBaffBoff and adding some syntactic sugar for reaching the main location I finally got it running:
public class Location
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
public bool flagMainLocation { get; set; }
public int? mainLocationID { get; set; }
public virtual ICollection<Location> ChildLocations { get; set; }
public virtual Location mainLocation { get; set;}
}
public class myappContext : DbContext
{
public myappContext() : base("myappContext")
{
}
public DbSet<Location> Locations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Location>()
.HasMany(l => l.ChildLocations)
.WithOptional()
.HasForeignKey(l => l.mainLocationID);
}
}
See the working example on github https://github.com/kranz/selfRefModel
I'm trying to code first on asp.net membership provider. When code first creates the table, it generates all tables and appropriate relationship to the bridge table but it also create an additional relationship from the AspNet_User(one) to Aspnet_Role(many). Do you know why it doing this? There shouldn't be any relationship between user and role table.
public class Aspnet_Role
{
public Aspnet_Role()
{
Aspnet_Users = new HashSet<Aspnet_Users>();
}
[Key]
public Guid RoleId { get; set; }
public string RoleName { get; set; }
public string LoweredRoleName { get; set; }
public string Description { get; set; }
public virtual ICollection<Aspnet_Users> Aspnet_Users { get; set; }
}
public class Aspnet_Users
{
public Aspnet_Users()
{
Aspnet_Roles = new HashSet<Aspnet_Role>();
}
[Key]
public Guid UserId { get; set; }
public string UserName { get; set; }
public string LoweredUserName { get; set; }
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
public DateTime LastActivityDate { get; set; }
public virtual Aspnet_Membership Aspnet_Membership { get; set; }
public virtual ICollection<Aspnet_Role> Aspnet_Roles { get; set; }
}
public class StagingContext : DbContext
{
public DbSet<Aspnet_Role> Aspnet_Roles { get; set; }
public DbSet<Aspnet_Users> Aspnet_Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Aspnet_Users>()
.HasMany(r => r.Aspnet_Roles)
.WithMany()
.Map(m => m.ToTable("aspnet_UsersInRoles")
.MapRightKey("RoleId")
.MapLeftKey("UserId"));
}
}
I added the navigation property on the WithMany extension.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Aspnet_Users>()
.HasMany(r => r.Aspnet_Roles)
.WithMany(u => u.Aspnet_Users)
.Map(m => m.ToTable("aspnet_UsersInRoles")
.MapRightKey("RoleId")
.MapLeftKey("UserId"));
}
I'm having issues applying multiple relationships (or possibly foreignkey) on two POCO objects. I've got the first relationship many-to-many working and when the database is created it creates the three tables (Projects, Users and ProjectsUsers) needed for the relationship.
Code so far:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public User()
{
Name = new Name();
}
public int UserId { get; set; }
public string LoginId { get; set; }
public string Password { get; set; }
public Name Name { get; set; }
public ICollection<Project> ManagedProjects { get; set; }
}
public class ProjectConfiguration : EntityTypeConfiguration<Project>
{
public ProjectConfiguration()
{
HasMany(x => x.Users)
.WithMany(x => x.ManagedProjects);
}
}
public UserConfiguration()
{
HasMany(x => x.ManagedProjects)
.WithMany(x => x.Users);
}
Now I want to add an optional one-to-one relationship of Project.ManagingUser -> User. However, I can't seem to figure out how to indicate this in the configuration.
Code for what I think is needed:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public int? ManagingUserId { get; set; }
public User ManagingUser { get; set; }
public ICollection<User> Users { get; set; }
}
I don't think the User object needs to change.
This shows my last attempt on mapping the new relationship:
public ProjectConfiguration()
{
HasMany(p => p.Users)
.WithMany(u => u.Projects);
this.HasOptional(p => p.ManagingUser)
.WithOptionalDependent()
.Map(m=>m.MapKey("ManagingUserId"))
.WillCascadeOnDelete(false);
}
What is happening when the database is created, I now end up with only two tables (Projects and Users). And it looks like it is only trying to setup the one-to-one relationship.
Can someone tell me what I'm missing?
Richard I've not changed the UserConfiguration and below is the DbContext:
public class MyDbContext : DbContext
{
public MyDbContext() : base(Properties.Settings.Default.ConnectionString)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Project> Projects { get; set; }
}
You probably want WithMany instead of WithOptionalDependent - it's a one:many relationship, not a one:one.
HasOptional(p => p.ManagingUser)
.WithMany()
.HasForeignKey(m => m.ManagingUserId)
.WillCascadeOnDelete(false);
EDIT
I think you're missing the OnModelCreating override from the DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ProjectConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
}
I have a User that can have collection of users he likes...
Another user can have collection of users he likes....
If User A likes User B and if User B likes User A, then they get to hang out. I need to send each other their contact info. How do we represent such a model in Entity Framework Code First?
public class User
{
public int UserId { get; set; }
public int? UserLikeId { get; set; }
public virtual UserLike UserLike { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<User> LikeUsers { get; set; }
}
Is this model correct? I can't get this to work.
I've tried another way but that doesn't work too...
I tried to add collection of user to user table.
For ex :
public virtual ICollection<User> userlike { get; set; }
public class User
{
public int UserId { get; set; }
public virtual ICollection<UserLike> UserLikes { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public int LikeUserId { get; set; }
public virtual User LikeUser { get; set; }
}
I get this error when I try to add user and who they like:
Conflicting changes to the role 'UserLike_LikeUser_Target' of the relationship 'UserLike_LikeUser' have been detected.
What's the best way to represent such a model?
You don't really need a separate entity to describe the relationship, the object model below will do the trick:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int? ThisUserLikesId { get; set; }
public virtual User ThisUserLikes { get; set; }
public virtual ICollection<User> LikeThisUser { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(u => u.ThisUserLikes)
.WithMany(u => u.LikeThisUser)
.HasForeignKey(u => u.ThisUserLikesId);
}
}
Now let's say you have a UserId in your hand and want to find the other User who likes this user which this user also like him:
using (var context = new Context())
{
// For a given user id = 1
var friends = (from u in context.Users
where u.UserId == 1
from v in u.LikeThisUser
where v.UserId == u.ThisUserLikesId
select new
{
OurUser = u,
HerFriend = v
})
.SingleOrDefault();
ExchangeContactInfo(friends.OurUser, friends.HerFriend);
}
Update 1:
A self referencing many-to-many association will be mapped to database using a join table which require a different object model and fluent API altogether:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<User> ThisUserLikes { get; set; }
public virtual ICollection<User> UsersLikeThisUser { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(u => u.ThisUserLikes)
.WithMany(u => u.UsersLikeThisUser)
.Map(c =>
{
c.MapLeftKey("UserId");
c.MapRightKey("OtherUserId");
c.ToTable("UserLikes");
});
}
}
Update 2:
As I explained in this post, a many-to-many association cannot have a payload (e.g EventId), and if that’s the case then we have to break it down to two one-to-many associations to an intervening class and I can see you’ve correctly created this class (UserLike) to represent the extra information attached to your self-referencing many-to-many association but the associations from this intermediate class are not correct as we need to define exactly 2 many-to-one association from UserLike to User like I showed in the following object model:
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public virtual ICollection ThisUserLikes { get; set; }
public virtual ICollection UsersLikeThisUser { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int LikerId { get; set; }
public int LikeeId { get; set; }
public int EventId { get; set; }
public User Liker { get; set; }
public User Likee { get; set; }
public virtual Event Event { get; set; }
}
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
}
public class Context : DbContext
{
public DbSet Users { get; set; }
public DbSet Events { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasMany(u => u.ThisUserLikes)
.WithRequired(ul => ul.Liker)
.HasForeignKey(ul => ul.LikerId);
modelBuilder.Entity()
.HasMany(u => u.UsersLikeThisUser)
.WithRequired(ul => ul.Likee)
.HasForeignKey(ul => ul.LikeeId)
.WillCascadeOnDelete(false);
}
}
Now you can use the following LINQ query to retrieve all the users who like each other:
using (var context = new Context())
{
var friends = (from u1 in context.Users
from likers in u1.UsersLikeThisUser
from u2 in u1.ThisUserLikes
where u2.LikeeId == likers.LikerId
select new
{
OurUser = u1.UserId,
HerFriend = u2.LikeeId
})
.ToList();
}
Hope this helps.