I have two classes
public class Project
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int ManagerID { get; set; }
public int CoordID { get; set; }
[ForeignKey("ManagerID")]
public virtual Employee Manager { get; set; }
[ForeignKey("CoordID")]
public virtual Employee Coord { get; set; }
}
public class Employee
{
[Key]
public int EmpID { get; set; }
public string Name { get; set; }
[InverseProperty("ManagerID")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("CoordID")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
The ManagerID and CoordID map to the EmpID column of the Employee table.
I keep getting an error for Invalid Columns becauce EF is not able to map correctly. I think it is looking for wrong column.
I think InverseProperty is used to refer to the related navigation property, not the foreign key, e.g.
public class Employee
{
[Key]
public int EmpID { get; set; }
public int Name { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("Coord")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
Also, is there a reason why your names are ints and not strings?
Best guess would be to use fluent API in your context via OnModelCreating. By renaming the column, EF can't figure out the original object to map so it's confused. However, Fluent API allows you to manually specify the map using something like the following:
public class MyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(x => x.Manager)
.WithMany(x => x.ManagerProjects)
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Project>()
.HasRequired(x => x.Coord)
.WithMany(x => x.CoordProjects)
.HasForeignKey(x => x.CoordID);
}
}
Related
I'm trying to create a library of books.
I separated data and users context/database for security reasons (and clarity) but is it that useful?
Specially since BookOfUserEntity.Id should be really a composite key between UserId & BookId.
Something like :
public class BookOfUserEntity
{
[Key]
public Guid UserId { get; set; }
[Key]
public int BookId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BookOfUserEntity>()
.HasKey(b => new { b.UserId, b.BookId });
}
But then if I create a composite key what should be HistoryEntity.BookOfUserEntityId type & value?
I also separated BookEntity & BookOfUserEntity to avoid duplicated data and reuse what can be reused (MainCoverArtId etc...), but this added complexity - should I go back to a simpler model?
public class BookEntity
{
public int Id { get; set; }
public Guid? MainCoverArtId { get; set; }
...
public virtual List<BookOfUserEntity>? BookOfUserEntities { get; set; } // Only purpose is to know how many Users have this very book in their list.
}
public class BookOfUserEntity // Bad name here don't mention it
{
public int Id { get; set; }
public Guid UserId { get; set; }
public int BookId { get; set; }
public BookEntity Book { get; set; } // Useful? Should be virtual?
public List<HistoryEntity>? HistoryEntities { get; set; }
...
}
public class HistoryEntity
{
public int Id { get; set; }
public int BookOfUserEntityId { get; set; }
public BookOfUserEntity BookOfUserEntity { get; set; } // Same questions
public int Vol { get; set; }
public double Chapter { get; set; }
public DateTime? ReadDate { get; init; }
}
I have an existing database that I am trying to access via Entity Framework 4.3. Most tables and relationships haven't been a problem, but this set of tables is causing me a few issues which I can't seem to find an answer to.
Here are the (condensed) entities:
Customer
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
private int addressSourceTypeID = 2;
[NotMapped]
public int AddressSourceTypeID {
get { return addressSourceTypeID; }
set { addressSourceTypeID = value; } }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
Contract
public class Contract
{
public int ContractID { get; set; }
public string Name { get; set; }
private int addressSourceTypeID = 4;
[NotMapped]
public int AddressSourceTypeID {
get { return addressSourceTypeID; }
set { addressSourceTypeID = value; } }
public virtual int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
//public virtual ICollection<Address> Addresses { get; set; }
}
Address
public class Address
{
[Key]
public int AddressID { get; set; }
public int AddressSourceTypeID { get; set; }
[ForeignKey("Customer")]
public int SourceKey { get; set; }
public virtual Customer Customer { get; set; }
//public virtual Contract Contract { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
What I have above is two entities Customer and Contract that both can have child Address entities. Currently the Address entity is set up to be a child of the Customer entity and this works fine as there isn't a link to Contract from Address.
I have tried adding in Contract to the Address entity as I have done with the Customer entity as you can see from the commented out code segments. Unfortunatly this doesn't work, but I'm not surprised due to the reference to Customer in the Address ForeignKey annotation. I even tried to create specific version of the Address entity (i.e. CustomerAddress), but I get an error when more than one entity is attempting to bind to the same table.
I have also tried using ModelBuilder in the EF DBContext however my knowledge here is pretty limited and I'm not sure how to do it in this case.
Overall, what I need is the following:
Customer entity to have a collection of child Addresses.
Contract entity to have a collection of child Addresses.
The link between these 'parent' tables to the Address table uses the following:
Customer: CustomerID => Address: SourceKey AND Customer: AddressSourceTypeID (always 2) => Address: AddressSourceTypeID.
Contract: ContractID => Address: SourceKey AND Contract: AddressSourceTypeID (always 4) => Address: AddressSourceTypeID.
If anyone could help me or point me in the correct direction that would be great.
Thanks very much.
You can either have EF enforce your SourceKey attribute using Table per Hierarchy Inheritance - and then you're mapping will break, or you can enforce the SourceKey in your business logic and only have EF manage the main Address class.
If you have to maintain your current DB schema, I think having your business logic enforce your SourceKey as a disriminator is your only option:
public class Address
{
public int AddressID { get; set; }
public int AddressSourceTypeID { get; set; }
public int SourceKey { get; set; }
public virtual Contract Contract { get; set; }
public virtual Customer Customer { get; set; }
}
public class Contract
{
public Contract()
{
this.Addresses = new List<Address>();
}
public int ContractID { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Customer
{
public Customer()
{
this.Addresses = new List<Address>();
}
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
And this in your fluent mappings:
modelBuilder.Entity<Address>().HasOptional(t => t.Contract)
.WithMany(t => t.Addresses)
.HasForeignKey(d => d.SourceKey);
modelBuilder.Entity<Address>().HasOptional(t => t.Customer)
.WithMany(t => t.Addresses)
.HasForeignKey(d => d.SourceKey);
Alternatively - if you created a CustomerAddress and ContractAddress, you can using TPH inheritance enforce the SourceKey - but currently there's no way to map the Nav properties:
public abstract class Address
{
[Key]
public int AddressID { get; set; }
public int AddressSourceTypeID { get; set; }
public int SourceKey { get; set; }
}
public class CustomerAddress : Address
{
public virtual Customer Customer { get; set; }
}
public class ContractAddress : Address
{
public virtual Contract Contract { get; set; }
}
And this as your mapping:
modelBuilder.Entity<Address>()
.Map<ContractAddress>(m => m.Requires("AddressSourceTypeID").HasValue(2))
.Map<CustomerAddress>(m => m.Requires("AddressSourceTypeID").HasValue(4));
This will enforce AddressSourceTypeID as your discriminator - unfortunately the breakdown here is in mapping your nav property back to the ContractAddress and Customer Address. See this related post which had the same basic problem. Maybe this will start you in the right direction at least.
I'm having trouble wrapping my head around a certain code-first relationship. I have three entities: Group, User, GroupPermission. The GroupPermission entity holds information about permissions that relate to a group. There are three permissions: leader, editor, user. The GroupPermission table should include the primary key Id and the name of the permission. Then I want a relationship table that looks something like this: Id - Group_Id - User_Username - GroupPermission_Id. There can be multiple groups, multiple users, multiple permissions. I have plenty of examples that help me make a single relationship table, but I can't find anything that includes multiple relationships.
Here are my entities...
User:
public class User
{
[Key, StringLength(EntityLength.UsernameLength)]
public string Username { get; set; }
[Required, StringLength(EntityLength.NameLength)]
public string FirstName { get; set; }
[Required, StringLength(EntityLength.NameLength)]
public string LastName { get; set; }
[Required, StringLength(EntityLength.Email)]
public string Email { get; set; }
public bool Active { get; set; }
public DateTime DateCreated { get; set; }
public virtual UserPermission UserPermission { get; set; }
public virtual ICollection<Group> Groups { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection<Issue> Issues { get; set; }
public virtual ICollection<GroupPermission> GroupPermissions { get; set; }
public string FullName
{
get { return FirstName + ' ' + LastName; }
}
}
Group:
public class Group
{
[Key]
public int Id { get; set; }
[Required, StringLength(EntityLength.GenericLength)]
public string Name { get; set; }
[Required, StringLength(EntityLength.DescriptionLength)]
public string Description { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection<GroupPermission> GroupPermissions { get; set; }
}
GroupPermission:
public class GroupPermission
{
[Key]
public int Id { get; set; }
[StringLength(EntityLength.GenericLength)]
public string Name { get; set; }
public int GroupId { get; set; }
public virtual ICollection<Group> Groups { get; set; }
public int UserId { get; set; }
public virtual ICollection<User> Users { get; set; }
public enum Permission
{
Leader = 1,
Editor = 2,
User = 3
}
}
When the tables are created using this structure, I get a GroupPermissions table that has Id, Name, GroupId, and UserId. This table needs to only be Id and Name. Then it creates a GroupPermissionUsers table that holds GroupPermissions_Id and User_Username. This is the table that should be Id, Group_Id, User_Username, GroupPermission_Id.
Does anybody have any tips to accomplish this or am I thinking about the design of this incorrectly?
In such case you are missing additional entity. It should look like:
New Permission entity with Id and Name:
public class Permission
{
[Key]
public int Id { get; set; }
[StringLength(EntityLength.GenericLength)]
public string Name { get; set; }
public virtual ICollection<GroupPermission> GroupPermissions { get; set; }
}
Modified GroupPermission entity to form junction table among Users, Groups and Permissions:
public class GroupPermission
{
[Key]
public int Id { get; set; }
public int GroupId { get; set; }
public virtual Group Group { get; set; }
public string UserName { get; set; }
[ForeignKey("UserName")]
public virtual User User { get; set; }
public int PermissionId { get; set; }
public virtual Permission Permission { get; set; }
}
I am working on a project in ASP.NET MVC using EF4 Code-First for the modeling. I have the following model classes:
public class ComicBook
{
public long ComicBookID { get; set; }
public string IssueTitle { get; set; }
public int IssueNumber { get; set; }
public DateTime PublishDate { get; set; }
public IList<ComicBookPerson> Writers { get; set; }
public IList<ComicBookPerson> Pencillers { get; set; }
public IList<User> CollectingUsers { get; set; }
}
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WorkedOnComicBooks { get; set; }
}
public class User
{
[Key]
public long UserID { get; set; }
public string Email { get; set; }
public IList<ComicBook> CollectedBooks { get; set; }
}
And the following DbContext:
public class ComicContext : DbContext
{
public DbSet<ComicBook> ComicBooks { get; set; }
public DbSet<ComicBookPerson> ComicBookPerson { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}
}
The rules that need to apply are:
Users can have many ComicBooks in their collection
ComicBooks can be in many users' collections
Each ComicBook can have 1 or more Writers
Each ComicBook can have 1 or more Pencillers
A ComicBookPerson can be a Writer or a Penciller on any book
A ComicBookPerson can work on many books, as a Writer, Penciller, or both
The many-to-many schema between Users and ComicBooks is created fine. It is the many-to-many between ComicBooks and ComicBookPersons that is breaking. The error message I get is:
Schema specified is not valid. Errors:
(15,6) : error 0040: Type ComicBook_Writers is not defined in namespace Comics.Models (Alias=Self).
I basically want two join tables, one called ComicBook_Writers and one called ComicBookPencillers. Both tables will contain a ComicBookID and a ComicBookPersonID. Is this possible in EF Code First?
You need two collection properties in ComicBookPerson class that relates to writers and pencillers.
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
}
Model mapped as
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WroteComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.PenciledComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}
I think this model is close to be right, but still missing something.
The ComicBookPerson can be both writer and penciller, that means you still need a third list in your class ComicBookPerson:
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
public IList<ComicBook> WrotPencComicBooks { get; set; }
Am I right?
I've have two tables in my project which are User and InvoiceLine.
It has been specified that an InvoiceLine can have a User known as a Checker.
My models are:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties
public virtual User Checker{ get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string CreatedByUserName { get; set; }
public DateTime CreatedDateTime { get; set; }
public string LastModifiedByUserName { get; set; }
public DateTime? LastModifiedDateTime { get; set; }
//Navigation properties
public virtual ICollection<InvoiceLine> InvoiceLines { get; set; }
}
So this was fine I have a 0..1 to many relationship from User to InvoiceLine.
This meant with Linq I could get the InvoiceLines the User needs to check via:
user.InvoiceLines
However there is another requirement that an InvoiceLine also has an Auditor so I modified the InvoiceLine to:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual int? AuditorId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties}
public virtual User Checker { get; set; }
public virtual User Auditor { get; set; }
}
So what I was really wanting was to go:
user.InvoiceLines
and get the Checkers and Auditors or alternatively get them seperately via:
user.CheckerInvoiceLines
user.AuditorInvoiceLines
I'm getting null back from user.InvoiceLines though which is understandable.
Could someone please point me in the right direction on how to use Linq to get the InvoiceLines from the User?
Edit Update:
My model configuration code is like:
public class VectorCheckContext : DbContext
{
...
public DbSet<InvoiceLine> InvoiceLines { get; set; }
public DbSet<User> Users { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
You need to use fluent mappings to configure the relationships when EF can not resolve them by conventions.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//other mappings
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Checker)
.WithMany(u => u.CheckerInvoiceLines)
.HasForeignKey(i => i.CheckerId);
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Auditor)
.WithMany(u => u.AuditorInvoiceLines)
.HasForeignKey(i => i.AuditorId);
}