I have stuck with a problem where I can't get access to releted objects.
Table Customers
CustomerId | CurrencyId | AreaId
4 | 1 | 4
Table CustomersToArea
CustomerAreaId | AreaId
1 | 4
And what I'm trying to do is to get CurrencyId using AreaId as a key From table CustomersToArea to Customers table:
public class Customer
{
public int CustomerId {get; set}
public int CurrencyId {get; set}
public int AreaId {get; set}
}
public class CustomersToArea
{
public int CustomersToAreaId {get; set}
//and here I'd like to have access to Customers
//and be able to get CurrencyId
//I', trying this way, but does not work:
public virtual Customer Customer {get; set;}
}
Actually I don't know how to tell to entity framework, that these both tables should be joined using AreaId.
I don't want any additional table, if this is not possible maybe any workaround exists?
The schema would indicate that an Area table would exist. I'm not too sure what the role of "CustomerToArea" would be given Customer already has an AreaId in it. Normally something like that would be a Many-to-Many joining table.
Based on the schema provided, and assuming there is an Area table: Map the relationships between Customer, Area, and CustomerToArea. For instance:
public class Customer
{
public int CustomerId {get; set;}
public int CurrencyId {get; set;} // If there is a currency entity, I'd recommend mapping it instead.
// ...
public virtual Area {get; set;}
}
public class Area
{
public int AreaId {get; set;}
// ...
public virtual ICollection<CustomerToArea> CustomerToAreas {get; set;} = new List<CustomerToArea>();
}
public class CustomerToArea
{
public int CustomerToAreaId {get; set;}
// ...
public virtual Area {get; set;}
}
public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
{
ToTable("Customers");
HasKey(x => x.CustomerId)
.Property(x => x.CustomerId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Area)
.WithMany()
.Map(x => x.MapKey("AreaId")); // Maps the FK on Customer without needing to declare it in the entity. If declared, use .HasForeignKey(x => x.AreaId)
}
}
public class AreaConfiguration : EntityTypeConfiguration<Area>
{
public AreaConfiguration()
{
ToTable("Areas");
HasKey(x => x.AreaId)
.Property(x => x.AreaId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.CustomerAreas)
.WithRequired(x => x.Area)
.Map(x =>x.MapKey("AreaId"); // Maps the FK on CustomerArea.
}
}
public class CustomerAreaConfiguration : EntityTypeConfiguration<CustomerArea>
{
public AreaConfiguration()
{
ToTable("CustomerAreas");
HasKey(x => x.CustomerAreaId)
.Property(x => x.CustomerAreaId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Then if you wanted to get Customer's CurrencyId by a CustomerAreaId...
int customerAreaId = 12;
var customers = dbContext.Customers
.Where(x => x.Area.CustomerAreas
.Any(a => a.CustomerAreaId == customerAreaId))
.Select(x => x.CurrencyId)
.ToList();
Alternatively, remove the CustomerToArea table and just call it "Area" with a PK on the AreaId. What you've outlined for a schema seems broken. If there is no Area table, and you see no point in adding it then my recommendation would be to fix the schema relations rather than break EF's relational mappings.
Related
I have the following class and table structure:
A component can have 1 Person (PersonID)
A component can have 1 Group (GroupID)
A component can have 1 Person and 1 Group
A Person always has a Component
A Group always has a Component
public class Component{
public Person Person {get; set;}
public Group Group { get; set; }
}
public class Person{
public Component Component { get; set; }
}
public class Group{
public Component Component { get; set; }
}
And I map this structure in this way:
public ComponentMap()
{
this.ToTable("Component")
.Property(x => x.Id)
.HasColumnName("ComponentID");
}
public GroupMap()
{
this.ToTable("Group")
.Property(x => x.Id)
.HasColumnName("GroupID");
this.HasRequired(x => x.Component)
.WithOptional(x => x.Group);
}
public PersonMap()
{
this.ToTable("Person")
.Property(x => x.Id)
.HasColumnName("GroupID");
this.HasRequired(x => x.Component)
.WithOptional(x => x.Person);
}
If I select a Group or Person the related Component is always fetched correctly but when I select a Component, the Group ID and Person ID are fetched using the Component ID and not the relative PersonID or GroupID. Where I can say that the Table Component has PersonID and GroupID? Inside the Component Map?
The tables are
**Component**
ComponentID
PersonID
GroupID
**Person**
PersonID
**Group**
GroupID
The only solution I got to make this working correctly is the following Mapping
this.ToTable("Component")
.Property(x => x.Id)
.HasColumnName("PartyID");
this.HasRequired(x => x.Person)
.WithRequiredDependent(x => x.Component)
.Map(cfg => cfg.MapKey("PersonID"));
this.HasRequired(x => x.Organization)
.WithRequiredDependent(x => x.Component)
.Map(cfg => cfg.MapKey("OrganizationID"));
Adding Properties of type FK on an entity doesn't make any difference than mapping using strings, I think is also wrong because expose useless ID on my Domain objects
If you define the FK as one of the properties in the class EF is smart enough to map it for you. You need to follow the convention of using the property name and append 'Id' to the end of it.
public class Component
{
public Person Person {get; set;}
public int PersonId {get;set;}
public Group Group { get; set; }
public int GroupId {get;set;}
}
public class Person{
public Component Component { get; set; }
public int ComponentId {get;set;}
}
public class Group
{
public Component Component { get; set; }
public int ComponentId {get;set;}
}
Is there a reason you're doing the mapping manually instead of using migrations?
I'm attempting to perform a db lookup using EF5 code-first. The basic structure and table relationships are as follows;
public partial class Member
{
public int RecordID {get; set;}
public string Name {get; set;}
...etc.
public virtual ICollection<MemberLink> MasterLinks {get; set;}
public virtual ICollection<MemberLink> SlaveLinks {get; set;}
public virtual ICollection<Message> ReceivedMessages {get; set;}
public virtual ICollection<Message> SentMessages {get; set;}
}
public partial class MemberLink
{
public int RecordID {get; set;}
public virtual Member MasterMember {get; set;}
public virtual Member SlaveMember {get; set;}
...etc.
}
public partial class Message
{
public int RecordID {get; set;}
public virtual Member Sender {get; set;}
public virtual Member Recipient {get; set;}
...etc.
}
Now, the query I'm trying to perform is using the MemberLinkRepository, and looks like;
public IList<MemberLink> GetMasterLinks(int p_MemberID)
{
return Get()
.Include ( memberLink => memberLink.MasterMember )
.Include ( memberLink => memberLink.SlaveMember )
.Include ( memberLink => memberLink.MasterMember.ReceivedMessages
.Where(
msg => msg.Sender.RecordID == memberLink.SlaveMember.RecordID) )
.Where ( memberLink => memberLink.MasterMember.RecordID == p_MemberID)
.ToList();
Except EF doesn't seem to like the nested Where. I could split this out into 2 separate repository calls (and indeed, it's looking like I might have to do that) but in the interest of reducing calls to the db I'm trying to do it in one foul swoop. Does anyone know how I can achieve this in one single query?
I hope the code illustrates what I'm trying to do... If not, I'll try and explain a little better.
The short answer is no, EF will not let you do that using Include().
Think about the result if it let you do this: in one case your MemberLink.MasterMember.ReceivedMessages will be fully populated, on another identical looking object MemberLink.MasterMember.ReceivedMessages is actually a sub-set of messages! What happens if you try to add to the ReceivedMessages? What if the addition doesn't match the filter? It is a bag of hurt.
The answer is to use projections:
public IList<MemberLinkWithFiltereredMessages> GetMasterLinks(int p_MemberID)
{
return Get()
.Include(memberLink => memberLink.MasterMember)
.Include(memberLink => memberLink.SlaveMember)
.Where(memberLink => memberLink.MasterMember.RecordID == p_MemberID)
.Select(memberLink => new MemberLinkWithFilteredMessages
{
MemberLink = memberLink,
FilteredMessages = memberLink.MasterMember.ReceivedMessages
.Where(msg => msg.Sender.RecordID == memberLink.SlaveMember.RecordID)
})
.ToList();
}
What you are really doing is asking for a specific sub-set of information, so be explicit about it.
I have read through several threads on StackOverflow and have not been able to figure this out. I am hoping someone can offer some advice. I have some POCO classes that look like this:
Person
{
int PersonCode {get; set;}
...
virtual List<PersonContact> {get; set;}
}
PersonContact
{
int PersonPersonCode {get; set;}
int ContactPersonCode {get; set;}
int PersonContactTypeCode {get; set;}
virtual PersonContactType {get; set;}
virtual Person Person {get; set;} // not sure I really need this one
virtual Person Contact {get; set;}
}
Each Person record will have zero to many PersonContact records. Each PersonContact record links one Person record to one other Person record and indicates the type of relationship between the two Person records with the PersonContactTypeCode.
I need to be able to map this so that a Person record can be navigated to his related PersonContact records. Something like this:
var john = new Person(...);
var david = new Person(...);
john.PersonContacts.Add(new PersonContact
{
Contact = david,
PersonContactType = ... // manager
});
and then
john.PersonContacts
.Where(c => c.PersonContactType.PersonContactTypeCode == "manager")
.FirstOrDefault();
would return
david
I have tried so many combinations of Data Annotations and Fluent API that I can hardly remember where I started. I seemed to have the best luck with this combination:
modelBuilder.Entity<Person>()
.HasMany(entity => entity.PersonContacts)
.WithRequired(person => person.Person)
.HasForeignKey(xref => xref.PersonPersonCode)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Person>()
.HasMany(entity => entity.PersonContacts)
.WithRequired(xref => xref.Contact)
.HasForeignKey(entity => entity.ContactPersonCode)
.WillCascadeOnDelete(false);
But, when I try to add more than one PersonContact to a Person, I get this error:
Multiplicity constraint violated. The role 'Person_PersonContacts_Source' of the
relationship '...Entities.Person_PersonContacts' has multiplicity
1 or 0..1.
I really appreciate any help, I am just completely stumped right now. By the way, I am open to changing these POCOs if necessary.
I'd guess it's because you are using the same navigation property to link to PersonContact.Person and PersonContact.Contact.
Assuming this:
Person
{
int PersonCode {get; set;}
...
virtual ICollection<PersonContact> PersonContacts {get; set;}
}
Try something like:
modelBuilder.Entity<PersonContact>()
.HasRequired(x => x.Person)
.WithMany(x => x.PersonContacts)
.HasForeignKey(x => x.PersonPersonCode)
.WillCascadeOnDelete(false);
modelBuilder.Entity<PersonContact>()
.HasRequired(x => x.Contact)
.WithMany()
.HasForeignKey(x => x.ContactPersonCode)
.WillCascadeOnDelete(false);
Try this:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId {get; set;}
...
public virtual ICollection<PersonContact> PersonContacts {get; set;}
}
public class PersonContact
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContactId {get; set;}
[ForeignKey("Person"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PersonId {get; set;}
public virtual Person Person {get; set;}
}
I have used Property Mapping instead of Fluent Mapping like you tried in your attempt.
If you have any questions let me know. As far as the relationship between your two Entities, this is what you need.
I managed to get something similar working using something like this
in my domain classes
public class PersonContact
{
public int Id { get; set; }
[Required]
public virtual Person ContactPerson { get; set; }
[Required]
public virtual Person Person { get; set; }
[Required]
public int ContactType { get; set; }
}
public class Person
{
int Id { get; set; }
private readonly List<PersonContact> _contacts = new List<Contact>();
public virtual List<PersonContact> Contacts
{
get
{
return this._contacts;
}
}
}
in my context
public DbSet<Person> People { get; set; }
public DbSet<PersonContact> Contacts { get; set; }
I made this change in a migration , and had to edit the generated create table code to set
Cascade delete to false for the fwo Foreign Keys to the person table, inside the PersonContact Table.
I get an extra Person_Id1 column in the PersonContact table. It seems to populate with the same data as Person_Id. This seems to be needed by EF when I create a bindingsource - as I get errors without it.
I wouldn't put explicit Foreign keys in, let the migration create them.
Hi guys I have a question about EF Fluent API (Code First :P). In my model I have
public class TABLE_A
{
public virtual long Id {get; set;}
....
public virtual TABLE_B MyTableBRef {get; set;}
}
public class TABLE_B
{
public virtual long Id {get; set;}
....
public virtual TABLE_A MyTableARef {get; set;}
}
How am I supposed to map a 0..1 to 0..1 relationship?
In the database on TABLE_B I have a column (FK) that references the PK of TABLE_A.
It is not possible to have navigational properties on both sides if you are using a column other than the PK of TABLE_B.
public class TABLE_A
{
public virtual long Id {get; set;}
}
public class TABLE_B
{
public virtual long Id {get; set;}
....
public virutal TABLE_A MyTableARef {get; set;}
}
public class MyContext : DbContext
{
public DbSet<TABLE_A> As { get; set; }
public DbSet<TABLE_B> Bs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TABLE_B>()
.HasRequired(b => b.MyTableARef)
.WithMany()
.Map(b => b.MapKey("FK_Column_name));
}
}
If the PK of TABLE_B is also a FK to TABLE_A then you can use shared PK mapping with navigational properties on both sides.
Edit:
You can use Shared PK mapping as follows
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TABLE_B>()
.HasRequired(b => b.MyTableARef)
.WithOptional(a => a.MyTableBRef);
}
The Id of TABLE_B is also an FK to TABLE_A.
I'm working to solve a very tedious problem.
I have a class called Nation and a class called NationAlly
public class Nation
{
public int ID {get; set;}
public int name {get;set;}
public List<NationAlly> NationAllies {get;set;}
}
public class NationAlly
{
public int ID {get; set;}
public int level {get;set;}
public Nation toNation {get;set;}
}
I'm using EF 4 and CodeFirst with a DbContext called NationsDB to manage my database on SQL Server 2008.
If I create a new object of type Nation and I try to call nationsDB.SaveChanges, I got the following exception:
"Multiplicity constraint violated. The role 'NationAlly_toNation_Target' of the relationship 'CodeFirstNamespace.NationAlly_toNation' has multiplicity 1 or 0..1."
I tried to save a Nation with NationAllies field null and this exception is not thrown, the nation table in the database gets all the correct values.
In my database the table Nation has 2 fields: ID(primary key), name
The table NationAlly has 3 fields: ID(primary key), level, NationID
The two tables are linked with a relationship where NationAlly.NationID is foreign key and Nation.ID is primary key.
Isn't strange? In my eyes the table NationAlly should have a field called NationID1 and another called NationID2 to create the "relationship" between a nation and a list of other nations.
What did I do wrong?
You are perhaps a victim of the EF Code-First mapping conventions which create automatically a relationship between NationAllies and toNation you don't want to have.
If I understand you correctly (but I am not 100 percent sure, if I do), you actually want to have two relationships and you have exposed only one end of the relationship in each of the entities. So, NationAllies does NOT point to toNation but to an "invisible" Owner nation in your NationAlly entity.
If that is the case you need to explicitly overwrite the convention mappings. In the Fluent API of EF 4.1 this could look like:
public class MyContext : DbContext
{
public DbSet<Nation> Nations { get; set; }
public DbSet<NationAlly> NationAllies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey("OwnerID"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey("NationID"))
.WillCascadeOnDelete(false);
}
}
This mapping would create the two foreign keys OwnerID and NationID in the NationAllies table, both pointing to the primary key ID in the Nations table.
Edit
Here is the application I have tested with:
Create a new Console App in VS2010 / .NET 4.0, name it "NationsApp"
Add a reference to "EntityFramework.dll"
Clear the content of "Program.cs" and paste instead the following in:
Content of Program.cs:
using System;
using System.Collections.Generic;
using System.Data.Entity;
namespace NationsApp
{
public class Nation
{
public int ID { get; set; }
public int name { get; set; }
public List<NationAlly> NationAllies { get; set; }
}
public class NationAlly
{
public int ID { get; set; }
public int level { get; set; }
public Nation toNation { get; set; }
}
public class NationsContext : DbContext
{
public DbSet<Nation> Nations { get; set; }
public DbSet<NationAlly> NationAllies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey("OwnerID"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey("NationID"))
.WillCascadeOnDelete(false);
}
}
class Program
{
static void Main(string[] args)
{
using (var context = new NationsContext())
{
try
{
// We have three Nations and two Allies
Nation nation1 = new Nation() {
NationAllies = new List<NationAlly>() };
Nation nation2 = new Nation() {
NationAllies = new List<NationAlly>() };
Nation nation3 = new Nation() {
NationAllies = new List<NationAlly>() };
NationAlly ally1 = new NationAlly();
NationAlly ally2 = new NationAlly();
// Nation1 has two Allies
// (Nation1 is the "owner" of both Allies)
nation1.NationAllies.Add(ally1);
nation1.NationAllies.Add(ally2);
// toNation of ally1 refers to Nation2
ally1.toNation = nation2;
// toNation of ally2 refers to Nation3
ally2.toNation = nation3;
context.Nations.Add(nation1);
context.Nations.Add(nation2);
context.Nations.Add(nation3);
context.SaveChanges();
}
catch (Exception e)
{
throw;
}
}
}
}
}
You can set a breakpoint on "throw" to watch possible exceptions in e in the debugger.
This creates a database called NationsApp.NationsContext if you are using SQL Server Express and don't have any further connection strings defined.
It gives two relationships Nation_NationAllies (FK is "OwnerID") and NationAlly_toNation (FK is "NationID"). All columns are non-nullable. The result in the DB is the following:
In case this helps someone getting this error... I was getting this message while doing queries rather than saving to the database. My data design:
public class Base {
public int Id {get; set;}
}
public class Child {
[Key][ForeignKey("Base")] public int Id {get; set;}
public virtual Base Base {get; set;}
public Child() {
Base = new Base();
}
}
The problem was in the constructor. Turns out EF4.1 doesn't like when you initialize associations there! I removed that constructor and things started working again.