Conflict with relationship and foreign key 1-1 - entity-framework

I'm trying to do this relationship
public class Company {
public int Id { get; set; }
public Configuration Configuration { get; set; }
}
public class Configuration {
public int Id { get; set; }
// public int CompanyId { get; set; } -> can't do this
public Company Company { get; set; }
}
public class ConfigurationMapping : EntityTypeConfiguration<Configuration> {
public ConfigurationMapping {
HasRequired(configuration => configuration.Company)
.WithOptional(company => company.Configuration)
// .HasForeignKey(configuration => configuration.CompanyId) -> this doesn't exist
.Map(f => f.MapKey("CompanyId")); // that's why I can't use the property above
}
}
I can't understand how I can add a Configuration and set the IdCompany.
There's another approach?
How can I do this?
db.Configurations.Add(new Configuration {
IdCompany = idCompany
});
db.SaveChanges();

You cannot. One-to-one relation is currently supported only if dependent entity has FK to principal entity as its primary key. It means that Configuration.Id is PK of Configuration but also FK to Company.Id.
The reason why you cannot use CompanyId is that database would require it to use unique index (otherwise it would not be one-to-one relation but one-to-many) and EF currently doesn't support unique indexes.
Edit:
Sorry. Now I better understand your question. If you know the Id of the company and you want to add it a new configuration you can try to do something like this:
var company = new Company { Id = companyId };
context.Companies.Attach(company); // Existing company, EF know thinks it was loaded from DB
var configuration = new Configuration { Company = company }; // Create relation with existing company. It must not be related to other configuration!
context.Configurations.Add(configuration);

Related

Adding an entity without knowing the parent id

I need to insert a new entity without knowing it's PK. The parent entity has another property which is a guid and unique which is what we use to do cross db references and this is all I have. I have done it in the past but can't find a reference on how to do it again.
[Table("School")]
public class SchoolEntity
{
public SchoolEntity()
{
Students = new HashSet<StudentEntity>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public Guid ExternalId { get; set; }
[ForeignKey("SchoolId")]
public virtual ICollection<StudentEntity> Students { get; set; }
}
[Table("Student")]
public class StudentEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public SchoolEntity School { get; set; }
}
//ExternalId won't work cause is not the primary key.
var school = new School { ExternalId = Guid.Parse('68e05258-550a-40f3-b68a-5d27a0d825a0') };
context.Attach(school);
context.Schools.Add.Add(new Student());
context.SaveChanges();
Well, the PK of the referenced entity is required in order to set properly the FK of the referencing entity.
If you don't have it, apparently you should find it (get it from the database) based on what you have (secondary identifier in your case). For instance:
var school = context.Schools.Single(e => e.ExternalId == externalId);
var student = new Student { School = school, ... };
context.Students.Add(student);
context.SaveChanges();
There is no way to get that working without fetching. If you don't want to fetch the whole referenced entity (and you are sure it's not tracked by the context), then you can fetch the PK only and Attach a stub entity:
var schoolId = context.Schools.Where(e => e.ExternalId == externalId)
.Select(e => e.Id).Single();
var school = new School( Id = schoolId);
context.Attach(school);
// ...

Mapping entities with fluent api on entity framework 5

I have a question.
I have these two tables:
The principal table is User with Customer dependence.
The reverse engineer code first generated classes as follows:
public class User
{
public User()
{
this.Customers = new List<Customer>();
}
...
public virtual ICollection<Customer> Customers { get; set; }
}
public class Customer
{
public Customer()
{
}
...
public int UserID { get; set; }
public virtual User User { get; set; }
}
I made the following modification in the user class:
public class User
{
public User()
{
}
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
Because the relationship is One-to–Zero-or-One.
The original mapping is this:
// Relationships
this.HasRequired(t => t.User)
.WithMany(t => t.Customers)
.HasForeignKey(d => d.UserID);
And the modified mapping is this :
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer)
.Map(m => m.MapKey("UserID"));
Is That correct?
If not, how would this mapping?
Thanks.
No, it's not correct.
The best thing you can do - supposed you can change the database schema - is removing the UserID foreign key from the Customer table and then create the relationship in the database between the two primary keys so that Customer.CustomerID is the foreign key in the association.
Reverse Engineering should then automatically create the expected one-to-one relationship, like so:
public class Customer
{
public int CustomerID { get; set; }
public virtual User User { get; set; }
//...
}
public class User
{
public int UserID { get; set; }
public virtual Customer Customer { get; set; }
//...
}
//...
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer);
If you can't change the database schema, your best bet is to only remove the collection ICollection<Customer> Customers from the User class and keep the relationship as one-to-many.
The reason for all this is that EF only supports shared primary key one-to-one associations, but not foreign key one-to-one associations. (The latter one you can only "fake" by removing the collection, but it's still one-to-many from EF viewpoint.)
You can read more about one-to-one associations with EF and its limitations here:
One-to-one Shared Primary Key Associations
One-to-one Foreign Key Associations

Entity Framework saving data in one-to-one associations

I'm trying to use the foreign key association approach to achieve a one-to-one association in EF. In my case there's an association between a User and a Team, and I need a navigation property in each of them.
I bumped into a problem when trying to save data.
This is what the models look like:
public class Team
{
public int ID { get; set; }
public string Name { get; set; }
public int OwnerID { get; set; }
public virtual User Owner { get; set; }
}
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public int TeamID { get; set; }
public virtual Team Team { get; set; }
}
I added these bits to the DbContext OnModelCreating(), as instructed in the blog post referenced above:
modelBuilder.Entity<User>()
.HasRequired(u => u.Team)
.WithMany()
.HasForeignKey(u => u.TeamID);
modelBuilder.Entity<Team>()
.HasRequired(t => t.Owner)
.WithMany()
.HasForeignKey(t => t.OwnerID)
.WillCascadeOnDelete(false);
And now when adding data like this:
User u = new User();
u.UserName = "farinha";
Team t = new Team("Flour Power");
u.Team = t;
t.Owner = u;
context.Users.Add(u);
context.Teams.Add(t);
context.SaveChanges();
or even like this:
User u = new User();
u.UserName = "farinha";
u.Team = new Team("Flour Power");
context.Users.Add(u);
context.SaveChanges();
I'm getting the following error:
Unable to determine a valid ordering
for dependent operations. Dependencies
may exist due to foreign key
constraints, model requirements, or
store-generated values.
Any idea of how to solve this? Am I saving the data in a wrong way?
Thanks in advance
You are not saving data wrong way but it simply cannot work because you defined bidirectional dependency. Team can be saved only if related to already saved user and user can be saved only if related to existing team. You must make one relation optional by marking foreign key property nullable (for example TeamId in User):
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public int? TeamID { get; set; }
public virtual Team Team { get; set; }
}
Then you must save the User entity first and then you can save Team.
User u = new User();
u.UserName = "farinha";
context.Users.Add(u);
context.SaveChanges();
u.Team = new Team { Name = "Flour Power", OwnerID = u.ID };
context.SaveChanges();

How do I get a delete trigger working using fluent API in EF CodeFirst CTP5?

I am having trouble getting referential integrity dialled down enough to allow my delete trigger to fire.
I have a dependent entity with three FKs. I want it to be deleted when any of the principal entities is deleted.
For principal entities Role and OrgUnit (see below) I can rely on conventions to create the required one-many relationship and cascade delete does what I want, ie: Association is removed when either principal is deleted.
For Member, however, I have multiple cascade delete paths (not shown here) which SQL Server doesn't like, so I need to use fluent API to disable cascade deletes.
Here is my (simplified) model:
public class Association
{
public int id { get; set; }
public int roleid { get; set; }
public virtual Role role { get; set; }
public int? memberid { get; set; }
public virtual Member member { get; set; }
public int orgunitid { get; set; }
public virtual OrgUnit orgunit { get; set; }
}
public class Role
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
public class Member
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
public class Organization
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
My first run at fluent API code looks like this:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
DbDatabase.SetInitializer<ConfDB_Model>(new ConfDBInitializer());
modelBuilder.Entity<Member>()
.HasMany(m=>m.assocations)
.WithOptional(a=>a.member)
.HasForeignKey(a=>a.memberId)
.WillCascadeOnDelete(false);
}
My seed function creates the delete trigger:
protected override void Seed(ConfDB_Model context)
{
context.Database.SqlCommand("CREATE TRIGGER MemberAssocTrigger ON dbo.Members FOR DELETE AS DELETE Assocations FROM Associations, deleted WHERE Associations.memberId = deleted.id");
}
PROBLEM --
When I run this, create a Role, a Member, an OrgUnit, and an Association tying the three together all is fine. When I delete the Role, the Association gets cascade deleted as I expect, same with OrgUnit. -- HOWEVER -- when I delete the Member I get an exception with a referential integrity error. I have tried setting ON CASCADE SET NULL because my memberid FK is nullable but SQL complains again about multiple cascade paths, so apparently I can cascade nothing in the Member-Association relationship.
To get this to work I must add the following code to Seed():
context.Database.SqlCommand("ALTER TABLE dbo.ACLEntries DROP CONSTRAINT member_associations");
As you can see, this drops the constraint created by the model builder.
QUESTION: this feels like a complete hack. Is there a way using fluent API for me to say that referential integrity should NOT be checked, or otherwise to get it to relax enough for the Member delete to work and allow the trigger to be fired?
Thanks in advance for any help you can offer. Although fluent APIs may be "fluent" I find them far from intuitive.
var listTriggers = new List<string>();
var listStoreProcedures = new List<string>();
using (var command = _context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "select group_concat(TRIGGER_NAME) from information_schema.TRIGGERS where TRIGGER_SCHEMA = 'yourschema'";
command.CommandType = CommandType.Text;
_context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var value = reader[0].ToString();
if (!string.IsNullOrEmpty(value))
listTriggers.AddRange(value.Split(","));
}
}
command.CommandText = "select group_concat(ROUTINE_NAME) from information_schema.ROUTINES where ROUTINE_TYPE = 'PROCEDURE' and ROUTINE_SCHEMA = 'yourschema'";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var value = reader[0].ToString();
if (!string.IsNullOrEmpty(value))
listStoreProcedures.AddRange(value.Split(","));
}
}
}
foreach (var item in listTriggers)
_context.Database.ExecuteSqlRaw($"drop trigger if exists {item}");
foreach (var item in listStoreProcedures)
_context.Database.ExecuteSqlRaw($"drop procedure if exists {item}");

how to annotate a parent-child relationship with Code-First

When using the CTP 5 of Entity Framework code-first library (as announced here) I'm trying to create a class that maps to a very simple hierarchy table.
Here's the SQL that builds the table:
CREATE TABLE [dbo].[People]
(
Id uniqueidentifier not null primary key rowguidcol,
Name nvarchar(50) not null,
Parent uniqueidentifier null
)
ALTER TABLE [dbo].[People]
ADD CONSTRAINT [ParentOfPerson]
FOREIGN KEY (Parent)
REFERENCES People (Id)
Here's the code that I would hope to have automatically mapped back to that table:
class Person
{
public Guid Id { get; set; }
public String Name { get; set; }
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
class FamilyContext : DbContext
{
public DbSet<Person> People { get; set; }
}
I have the connectionstring setup in the app.config file as so:
<configuration>
<connectionStrings>
<add name="FamilyContext" connectionString="server=(local); database=CodeFirstTrial; trusted_connection=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
And finally I'm trying to use the class to add a parent and a child entity like this:
static void Main(string[] args)
{
using (FamilyContext context = new FamilyContext())
{
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred"
};
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
Parent = fred
};
context.People.Add(fred);
var rowCount = context.SaveChanges();
Console.WriteLine("rows added: {0}", rowCount);
var population = from p in context.People select new { p.Name };
foreach (var person in population)
Console.WriteLine(person);
}
}
There is clearly something missing here. The exception that I get is:
Invalid column name 'PersonId'.
I understand the value of convention over configuration, and my team and I are thrilled at the prospect of ditching the edmx / designer nightmare --- but there doesn't seem to be a clear document on what the convention is. (We just lucked into the notion of plural table names, for singular class names)
Some guidance on how to make this very simple example fall into place would be appreciated.
UPDATE:
Changing the column name in the People table from Parent to PersonId allows the Add of fred to proceed. Howerver you'll notice that pebbles is a added to the Children collection of fred and so I would have expected pebbles to be added to the database as well when Fred was added, but such was not the case. This is very simple model, so I'm more than a bit discouraged that there should be this much guess work involved in getting a couple rows into a database.
You need to drop down to fluent API to achieve your desired schema (Data annotations wouldn't do it). Precisely you have an Independent One-to-Many Self Reference Association that also has a custom name for the foreign key column (People.Parent). Here is how it supposed to get done with EF Code First:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOptional(p => p.Parent)
.WithMany(p => p.Children)
.IsIndependent()
.Map(m => m.MapKey(p => p.Id, "ParentID"));
}
However, this throws an InvalidOperationException with this message Sequence contains more than one matching element. which sounds to be a CTP5 bug as per the link Steven mentioned in his answer.
You can use a workaround until this bug get fixed in the RTM and that is to accept the default name for the FK column which is PersonID. For this you need to change your schema a little bit:
CREATE TABLE [dbo].[People]
(
Id uniqueidentifier not null primary key rowguidcol,
Name nvarchar(50) not null,
PersonId uniqueidentifier null
)
ALTER TABLE [dbo].[People] ADD CONSTRAINT [ParentOfPerson]
FOREIGN KEY (PersonId) REFERENCES People (Id)
GO
ALTER TABLE [dbo].[People] CHECK CONSTRAINT [ParentOfPerson]
GO
And then using this fluent API will match your data model to the DB Schema:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOptional(p => p.Parent)
.WithMany(p => p.Children)
.IsIndependent();
}
Add a new Parent record containing a Child:
using (FamilyContext context = new FamilyContext())
{
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
};
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred",
Children = new List<Person>()
{
pebbles
}
};
context.People.Add(fred);
context.SaveChanges();
}
Add a new Child record containing a Parent:
using (FamilyContext context = new FamilyContext())
{
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred",
};
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
Parent = fred
};
context.People.Add(pebbles);
var rowCount = context.SaveChanges();
}
Both codes has the same effect and that is adding a new parent (Fred) with a child (Pebbles).
In Entity Framework 6 you can do it like this, note public Guid? ParentId { get; set; }. The foreign key MUST be nullable for it to work.
class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? ParentId { get; set; }
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
https://stackoverflow.com/a/5668835/3850405
It should work using a mapping like below:
class FamilyContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Person>().HasMany(x => x.Children).WithMany().Map(y =>
{
y.MapLeftKey((x => x.Id), "ParentID");
y.MapRightKey((x => x.Id), "ChildID");
});
}
}
However that throws an exception: Sequence contains more than one matching element.
Apperently that is a bug.
See this thread and the answer to #shichao question: http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx#10102970
class Person
{
[key()]
public Guid Id { get; set; }
public String Name { get; set; }
[ForeignKey("Children")]
public int? PersonId {get; set;} //Add ForeignKey
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
builder.Entity<Menu>().HasMany(m => m.Children)
.WithOne(m => m.Parent)
.HasForeignKey(m => m.PersonId);