Given the following simplified model:
public class Account
{
public Account()
{
Id = Guid.NewGuid();
ContactCard = new ContactCard();
}
//[ForeignKey("ContactCard")]
public Guid Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public ContactCard ContactCard { get; set; }
}
public class ContactCard
{
public ContactCard()
{
Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public Account Account { get; set; }
}
public class MightDbContext: DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<ContactCard> ContactCards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>().HasRequired(x => x.ContactCard).WithOptional(x => x.Account);
}
}
public class MightDbInitializer : DropCreateDatabaseIfModelChanges<MightDbContext>
{
protected override void Seed(MightDbContext context)
{
var accounts = new List<Account>
{
new Account()
{
Name = "Acme Corporation Pty. Ltd.",
Number = "001ABC"
},
new Account()
{
Name = "Three Little Pigs Pty. Ltd.",
Number = "002DEF"
}
};
accounts.ForEach(c => context.Accounts.Add(c));
}
}
And the following simple console program to iterate the contents of the Accounts and ContactCards collections:
static void Main(string[] args)
{
Database.SetInitializer<MightDbContext>(new MightDbInitializer());
using (var context = new MightDbContext())
{
foreach (Account c in context.Accounts)
{
Console.WriteLine(c.ContactCard.Id);
}
var contactCards = context.ContactCards.ToList(); /* ERROR OCCURS HERE */
foreach (ContactCard a in contactCards)
{
Console.WriteLine(a.Id);
}
}
Console.Read();
}
Why do I get the following error as soon as I try to access the ContactCards collection:
Multiplicity constraint violated. The role 'Account_ContactCard_Source' of the relationship 'InvestAdmin.Might.DataAccess.Account_ContactCard' has multiplicity 1 or 0..1.
When I look at the data that has actually been stored in the database tables all seems to be correct. In fact here is that data:
Accounts:
Id Name Number
ab711bad-1b32-42ca-b68b-12f7be831bd8 Acme Corporation Pty. Ltd. 001ABC
dc20a1dd-0ed4-461d-bc9c-04a85b555740 Three Little Pigs Pty. Ltd. 002DEF
ContactCards:
Id
dc20a1dd-0ed4-461d-bc9c-04a85b555740
ab711bad-1b32-42ca-b68b-12f7be831bd8
And for completeness here is the Account_ContactCard foreign key constraint as defined in the database:
-- Script Date: 06/12/2011 7:00 AM - Generated by ExportSqlCe version 3.5.1.7
ALTER TABLE [Accounts] ADD CONSTRAINT [Account_ContactCard] FOREIGN KEY ([Id]) REFERENCES [ContactCards]([Id]) ON DELETE NO ACTION ON UPDATE NO ACTION;
I have been reading all I can on defining one to one relationships in Code First and have tried many different configurations. All end up back at the same problem.
I found that I could resolve this problem by moving the creation of the associated ContactCard from the Account constructor into the actual creation of the Account objects in the Seed method of the DBInitializer. So it appears that it was not (directly) a problem with the One to One relationship.
Related
I had invest now one day to find a solution wihtout success.
I like to create a simple Many to Many relation.
For that I've created two Models:
A Player class where i will have the 'Turnaments' navigation property to see which turnaments has visit a player.
public class Player
{
[Key]
public Int64 PlayerId { get; set; }
public string FirstName { get; set; }
[Required]
public string Surename { get; set; }
public virtual ICollection<Turnament> Turnaments { get; set; }
}
and a Turnament class where I will have the 'Players' navigation property to see which players a part of the turnament.
public class Turnament
{
[Key]
public int TurnamentId { get; set; }
[Required]
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public bool IsClosed { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
If start the mirgation of by the command 'upate-database' (automtic migratons = on) at the 'Package Manager Console' window of VS2012 - I get the following exception:
Introducing FOREIGN KEY constraint 'FK_dbo.PlayerTurnaments_dbo.Turnaments_Turnament_TurnamentId' on table 'PlayerTurnaments' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Also the Background of that Exception I understand.
So I will reach to tell the entity-framwork that if I'm delete:
a) A Player EF should not push a cascade delete to the associated Turnament
b) A Turnament EF should push a cascade delete to the accociated Player
with that Background the cycles paths are gone.
So I was try to implement a 'Switch cascade delete off' via the fluent-API of Entity Framwork:
public class DataContext : DbContext
{
public DbSet<Turnament> Turnaments { get; set; }
public DbSet<Player> Players { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
//modelBuilder.Entity<Turnament>()
// .HasMany( b => b.Players )
// .WithMany( a => a.Turnaments )
// .Map( m => m.MapLeftKey( "PlayerPid" )
// .MapRightKey( "TurnamentPid" )
// .ToTable( "TurnamentsJoinPlayers" ) );
//modelBuilder.Entity<Player>().HasRequired( t => t.Turnaments ).
// WithMany().WillCascadeOnDelete( false );
//modelBuilder.Entity<Turnament>().HasRequired( t => t.Players ).
// WithMany().WillCascadeOnDelete( false );
//modelBuilder.Entity<Player>().HasRequired( t => t.TurnamentsRelation ).
// WithMany().HasForeignKey( p => p.PlayerId ).WillCascadeOnDelete( false );
//modelBuilder.Entity<Player>().HasMany(p => p.TurnamentsRelation).
base.OnModelCreating( modelBuilder );
}
}
But as you see everthing is commend out because its don't help.
thx for all competent help with more as only some code snippes that only cracks understand :)
PS: I'm sure there is a solution for EF5 (final) of that kind of many-to-many relations
SOLUTION
public class MyAppContext : DbContext
{
public MyAppContext()
{
// Somtimes helps to uncommend that line, then the Database will recreated!
// (For people that are not fit enough in EF CodeFirst - like me ;)
// 1) Uncommend
// 2) Start Application go on a site that access your data wait for
// Exception (No Database!)
// 3) Unkommend line
// 4) Execute 'update-database' at the 'Package Manager Console' of VS 2012
// Database.SetInitializer(new DropCreateDatabaseAlways<LivescoreContext>());
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
/* Players <-> Turnaments */
modelBuilder.Entity<Player>()
.HasMany( p => p.Turnaments )
.WithMany( p => p.Players )
.Map( mc =>
{
mc.MapLeftKey( "PlayerPid" );
mc.MapRightKey( "TurnamentPid" );
mc.ToTable( "PlayersJoinTurnaments" );
} );
base.OnModelCreating( modelBuilder );
}
}
I'm trying to remove a child entity from a parent collection navigation property. There is a one-to-many relationship set up b/t parent and child. Once I remove the child, I want the database to remove the assoc. child record from the database rather than orphaning that record by nullifying the foreign key.
Is there any way to do this without having to explicitly delete the child via the child DbSet in the DBContext?
I've seen other posts related to this topic, but I thought I'd distill the code down to a simpler test case:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using NUnit.Framework;
namespace basic_tests
{
[TestFixture]
public class OneToManyTests
{
#region Setup/Teardown
[SetUp]
public void SetUp()
{
_context = new Context();
Database.SetInitializer(new DataInitializer());
}
#endregion
private Context _context;
[Test]
public void CanRemoveChildThroughParent()
{
/**
this throws : "System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for
details.System.Data.UpdateException : A relationship from the 'Child_MyParent' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a
corresponding 'Child_MyParent_Source' must also in the 'Deleted' state.
**/
var parent = _context.Parents.FirstOrDefault();
var firstChild = parent.MyChildren.FirstOrDefault();
parent.MyChildren.Remove(firstChild);
_context.SaveChanges();
var parentRefresh = new Context().Parents.FirstOrDefault();
Assert.AreEqual(1, parentRefresh.MyChildren.Count);
var childrenCount = new Context().Children.Count();
Assert.AreEqual(1, childrenCount);
}
}
public class Parent
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Child> MyChildren { get; set; }
}
public class Child
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Parent MyParent { get; set; }
}
public class Context : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>()
.HasRequired(c => c.MyParent)
.WithMany(p => p.MyChildren)
.WillCascadeOnDelete(true);
}
}
public class DataInitializer : DropCreateDatabaseAlways<Context>
{
protected override void Seed(Context context)
{
for (var i = 0; i < 2; i++)
{
context.Children.Add(new Child
{
Name = "child" + i
});
}
var parent = new Parent { Name = "parent", MyChildren = context.Children.Local.ToList() };
context.Parents.Add(parent);
base.Seed(context);
}
}
}
Is there any way to do this without having to explicitly delete the
child via the child DbSet in the DBContext?
In your model: No, there is no other way. You have to call:
var parent = _context.Parents.FirstOrDefault();
var firstChild = parent.MyChildren.FirstOrDefault();
_context.Children.Remove(firstChild);
_context.SaveChanges();
I recently have learned that there is one exception which causes an automatic delete in the database when you remove the child from the parent collection. That's the so-called Identifying relationship which requires that the foreign key property in the child refering to the parent must be part of the (composite) primary key of the child:
public class Child
{
[Key, Column(Order = 0)]
public virtual int Id { get; set; }
[Key, ForeignKey("MyParent"), Column(Order = 1)]
public virtual int MyParentId { get; set; }
public virtual string Name { get; set; }
public virtual Parent MyParent { get; set; }
}
In this case your code would indeed delete the child from the database. It is explained here (last section at the bottom): http://msdn.microsoft.com/en-us/library/ee373856.aspx
var parent = context.Parents.Include(p=>p.Children).Where(p=>p.ParentId == 1);
foreach(Children child in parent.Children.ToList())
{
context.Entry(child).State = EntityState.Deleted;
}
context.Entry(parent).State = EntityState.Deleted;
context.SaveChanges();
For more info go here: https://stackoverflow.com/a/9571108/1241400
EDIT: Why less generic
public void DeleteMany<E>(IQueryable<E> entitiesToDelete) where E : class
{
foreach (var entity in entitiesToDelete)
{
DataContext.Entry(entity).State = System.Data.EntityState.Deleted;
}
}
and you'll just call it
var childrenToDelete = someRepository.FindChildrenByParentId<Children>(ParentId);
someRepository.DeleteMany<Children>(childrenToDelete);
I'm trying to model a self-referencing many to many in EF CodeFirst with a polymorphic table structure. I'm using the October 2011 CTP which supports navigation properties on derived types (which works well in other tests I've done).
The problem:
When I set up this particular many to many relationship in the base (abstract) table's mapping and try to get related records, I get a SQL query with hundreds of K of unions and joins...just the time taken to generate the SQL statement is 30 seconds, compared to bare milliseconds to execute it. However, it does return appropriate results. When I change the many to many to exist between two derived objects, the query produced is perfect...but I can't map the same relating M2M table again for other derived objects without being informed that the joining table has "already been mapped".
Specifics:
An existing database structure has a base table--Party--which is joined 1...1 or 0 with Customer, Vendor, User, and Department (each a type of Party).
Parties are related to each other via an existing join table PartyRelationship (ID, InternalPartyID, ExternalPartyID). By convention, InternalPartyID contains a User's PartyID and ExternalPartyID contains the PartyID of the Customer, Vendor, or Department with which they are associated.
Trying to use EF CodeFirst in a new project (WCF DataServices), I have created the Party class as:
public abstract class Party
{
public Party()
{
this.Addresses = new List<Address>();
this.PhoneNumbers = new List<PhoneNumber>();
this.InternalRelatedParties = new List<Party>();
this.ExternalRelatedParties = new List<Party>();
}
public int PartyID { get; set; }
public short Active { get; set; }
//other fields common to Parties
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<Party> InternalRelatedParties { get; set; }
public virtual ICollection<Party> ExternalRelatedParties { get; set; }
}
Then, using TPT inheritance, Customer, Vendor, Department and User are similar to:
public class Customer : Party
{
public string TermsCode { get; set; }
public string DefaultFundsCode { get; set; }
//etc
}
public class User : Party
{
public string EmployeeNumber { get; set; }
public string LoginName { get; set; }
//etc
}
The joining table:
public class PartyRelationship
{
public int PartyRelationshipID { get; set; }
public int InternalPartyID { get; set; }
public int ExternalPartyID { get; set; }
//certain other fields specific to the relationship
}
Mappings:
public class PartyMap : EntityTypeConfiguration<Party>
{
public PartyMap()
{
// Primary Key
this.HasKey(t => t.PartyID);
// Properties
this.ToTable("Party");
this.Property(t => t.PartyID).HasColumnName("PartyID");
this.Property(t => t.Active).HasColumnName("Active");
//etc
// Relationships
this.HasMany(p => p.InternalRelatedParties)
.WithMany(rp => rp.ExternalRelatedParties)
.Map(p => p.ToTable("PartyRelationship")
.MapLeftKey("ExternalPartyID")
.MapRightKey("InternalPartyID"));
}
}
public class PartyRelationshipMap : EntityTypeConfiguration<PartyRelationship>
{
public PartyRelationshipMap()
{
// Primary Key
this.HasKey(t => t.PartyRelationshipID);
// Properties
// Table & Column Mappings
//this.ToTable("PartyRelationship"); // Commented out to prevent double-mapping
this.Property(t => t.PartyRelationshipID).HasColumnName("PartyRelationshipID");
this.Property(t => t.InternalPartyID).HasColumnName("InternalPartyID");
this.Property(t => t.ExternalPartyID).HasColumnName("ExternalPartyID");
this.Property(t => t.CreateTime).HasColumnName("CreateTime");
this.Property(t => t.CreateByID).HasColumnName("CreateByID");
this.Property(t => t.ChangeTime).HasColumnName("ChangeTime");
this.Property(t => t.ChangeByID).HasColumnName("ChangeByID");
}
}
Context:
public class MyDBContext : DbContext
{
public MyDBContext()
: base("name=MyDBName")
{
Database.SetInitializer<MyDBContext>(null);
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Party> Parties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new PartyMap());
modelBuilder.Configurations.Add(new PartyRelationshipMap());
}
}
A URL such as http://localhost:29004/Services/MyDataService.svc/Parties(142173)/SAData.Customer/InternalRelatedParties eventually returns correct oData but takes 30 seconds to produce an enormous SQL statement (189K) that executes in 600 ms.
I've also tried mapping the PartyRelationship table with a bidirectional one to many (both to Party as the "one" table), but with a similar outcome.
Do I need separate join tables for Customer-User, Vendor-User, and Department-User? Should I look at vertical table splitting or database views that separates PartyRelationship into separate logical entities (so I can remap the same table)? Is there another way the EF model should be configured in this scenario?
public class Purchase {
public Address to { get; set; }
public Address from { get; set; }
}
public class Address {
public string name { get; set; }
}
I have 1 Purchase with 2 Address. How this should be on the Database (mySql) including foreign keys to be used on Entity Framework.
I have the problem that entity understands (based on fk) that Navigability in Address is 1 to many (*) and I don't have a list of Address on Purchase, i have defined 2.
Thanks,
Bart.
You can configure relationships of the tables.Build your model like this,
public class PurchaseConfiguration : EntityTypeConfiguration<Purchase >
{
public PurchaseConfiguration()
{
HasRequired(p=>p.to ).WithOptionalDependent().WillCascadeOnDelete(false);
HasRequired(p => p.from ).WithOptionalDependent().WillCascadeOnDelete(false);
}
}
and your db context you can add the configurations like this,
public class yourDbContext:DbContext
{
public DbSet<Purchase> Purchases{ get; set; }
public DbSet<Address> Addresses{ get; set; }
//other db sets here..
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PurchaseConfiguration ());
// you can add configurations for other tables
}
}
Using Entity Framework 4 and code first how would I create a model that supports this scenario:
In an application, there are users, each user belongs to one or more groups and for each group the user can have one or more roles.
Example:
I would like to be able to say, "give me Lisa", and the response returns a user object for lisa, with the groups she belongs to. For each group there is a list property with all the roles she has for that particular group
Can anyone help me model this using code first, any help/code samples, would be great!
/Best regards Vinblad
Edit: Here is new model for your requirement.
public class User
{
public virtual int Id { get; set; }
public virtual ICollection<UserPermission> Permissions { get; set; }
}
// Permission is extended junction table to model M:N between
// User and Group but in addition it contains relation to Roles.
// The ony disadvantage is that this model doesn't control that
// role in the collection is also the role related to group. You
// must either enforce it in application logic or create some additional
// database construct to check it.
public class UserPermission
{
public virtual int UserId { get; set; }
public virtual int GroupId { get; set; }
public virtual Group Group { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Group
{
public virtual int Id { get; set; }
public virtual ICollection<UserPermission> UserPermissions { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Role
{
public virtual int Id { get; set; }
public virtual ICollection<Group> Groups { get; set; }
public virtual ICollection<UserPermission> UserPermissions { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<UserPermission> UserPermissions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Permission has composite key
modelBuilder.Entity<UserPermission>()
.HasKey(p => new {p.UserId, p.GroupId});
// Permission doesn't have navigation property to user
modelBuilder.Entity<User>()
.HasMany(u => u.Permissions)
.WithRequired()
.HasForeignKey(p => p.UserId);
modelBuilder.Entity<Group>()
.HasMany(g => g.UserPermissions)
.WithRequired(p => p.Group)
.HasForeignKey(p => p.GroupId);
}
}
As described in code there is small disadvantage. You can avoid the disadvantage by enforcing data integrity in DB by additional FK which can't be modeled by code first. You can use custom initializer to add that FK:
public class CustomInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
context.Database.ExecuteSqlCommand(
#"ALTER TABLE [dbo].[RoleUserPermissions]
WITH CHECK ADD CONSTRAINT [FK_RoleUserPermissions_RoleGroups]
FOREIGN KEY([Role_Id], [UserPermission_GroupId])
REFERENCES [dbo].[RoleGroups] ([Role_Id], [Group_Id])");
}
}
Just add this to your application initialization (only for debug - application should not be able to drop its database in release):
Database.SetInitializer(new CustomInitializer());