I have created an MVC using Entity framework and I've encountered a situation which I don't know how to resolve.
I'm using the EF auto joins and relations (all my table models were created automatically by EF) .
Now for the problem - I have a table of customers, which has two(relavent) fields - personID and employerID . Only one of them contains data , the other will be null (a customer is either a person , or an employer) . When I try to include employer model in the result set, I'm getting thrown (without any message , when I debug I see that the content has data but the employeer is sometimes NULL) I'm also not sure about how the design should look like. This is my code :
Customer:
public partial class Customer
{
public Customer()
{
Account = new HashSet<Account>();
}
public long Id { get; set; }
public int? PersonId { get; set; }
public int Type { get; set; }
public int? EmployerId { get; set; }
public Employer Employer { get; set; }
public ICollection<Account> Account { get; set; }
}
Employer:
public partial class Employer
{
public Employer()
{
Customer = new HashSet<Customer>();
}
public int Id { get; set; }
public string Name { get; set; }
public int? IdType { get; set; }
public ICollection<Customer> Customer { get; set; }
}
Person:
public partial class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Sex { get; set; }
public DateTime? BirthDate { get; set; }
public int IdType { get; set; }
}
Now when I'm running in my repository:
var collectionBeforePaging = _context.Customer
Everything works, but Employer is NULL. If I use :
var collectionBeforePaging = _context.Customer.Include(a => a.Employer)
Then the project fails .
How can I make this joins?
Please define ForeignKey for Customer
public int? EmployerId { get; set; }
[ForeignKey(nameof(EmployerId))]
public virtual Employer Employer { get; set; }
What version of EF you use? I think you missing something like that :
In Employer :
[ForeignKey("EmployerId")]
[InverseProperty("Customers")]
public virtual Employer Employer { get; set; }
public int? EmployerId { get; set; }
In Customer :
[InverseProperty("Employer")]
public virtual ICollection<Customer> Customers { get; set; }
It can also be done in the Dbontext object
Related
i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}
I am trying to map a property on a user that that has a many to many relationship in the database but there is only ever one per user. But I am unable to figure out the required map in entityframework. I have the following entities:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//Need to map this property
public virtual SecurityRole SecurityRole { get; set; }
}
public class SecurityRole
{
public int Id { get; set; }
public string Name { get; set; }
}
An the following tables:
User:
Id
FirstName
LastName
SecurityRole:
Id
Name
UserSecurityRole:
UserId
SecurityRoleId
If anyone has any idea or could point me in the right direction that would be great
Even if there is only one record in the database, if you have a many to many relationship between User and SecurityRole it should work like this:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<SecurityRole> SecurityRoles { get; set; }
}
public class SecurityRole
{
public int Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
Hello had this problem before with another data type. For more details you can see here.
Multiplicity constraint violated Entity framework 5
But now that solution doesn't work or any other found on the net.
My classes are:
namespace Prometheus.Models
{
[Table("People")]
public class Person : IPerson, INamedEntity
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
[DisplayName("Prenume")]
public virtual string FirstName { get; set; }
[DisplayName("Nume")]
public virtual string LastName { get; set; }
public string Name
{
get
{
return FirstName + " " + LastName;
}
set { }
}
[DisplayName("Email")]
public virtual string Email { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<Result> Results { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
}
And
public class Project :INamedEntity
{
public int Id { get; set; }
public DateTime? LastModified { get; set; }
[DisplayName("Titlu Proiect")]
[Required]
public string Name { get; set; }//Title
[Required]
[DisplayName("Tip proiect")]
public virtual ProjectType ProjectType{ get; set; }
[DisplayName("Status proiect")]
public virtual ProjectStatus ProjectStatus { get; set; }
[Required]
[DisplayName("Tip program")]
public virtual ProgramType ProgramType { get; set; }
[Required]
[DisplayName("Numar luni")]
public int NumberOfMonths { get; set; }
[Required]
[DisplayName("Project title")]
public string NameEn { get; set; }
[Required]
[DisplayName("Acronim")]
public string Acronym { get; set; }
[Required]
[DisplayName("Numar contract")]
public string ContractNo { get; set; }
[Required]
[DisplayName("Domeniu de activitate")]
public virtual ActivityField ActivityField {get;set;}
[Required]
[DisplayName("Summary")]
public string SummaryEn { get; set; }
[Required]
[DisplayName("Rezumat")]
public string SumarryRo { get; set; }
[Required]
[DisplayName("Data inceput")]
public virtual DateTime StartDate { get; set; }
[Required]
[DisplayName("Data sfarsit")]
public virtual DateTime EndDate { get; set; }
[Required]
[DisplayName("Institutie coordonatoare")]
public virtual string CoordInstitution { get; set; }
[DisplayName("Valoare totala proiect")]
public decimal TotalValue { get; set; }
[DisplayName("Valuta")]
public virtual Currency Currency { get; set; }
[DisplayName("Rol in proiect")]
public virtual RoleInProject RoleInProject { get; set; }//coord proiect/partener/in afara inst
[DisplayName("Echipa proiect")]
public virtual ICollection<Person> People { get; set; }
[DisplayName("Director proiect")]
public virtual Person ProjectManager { get; set; }
public virtual ICollection<Institution> Partners { get;set;}
public virtual Person PersonInCharge { get; set; }
[DisplayName("Functie/Rol Expert In Proiect")]
public string UserFunctionInProject { get; set; }
[DisplayName("Pagina proiectului")]
[Required]
public string Website {get;set;}
public virtual ICollection<Result> Results { get; set; }
}
When i had the previous problem it was with a class Result and the same Person and was told that if i want a many-to-many relationship i have to add a Collection to Person and a Collection to Result and that did the trick.
But now as you can see i already have
public virtual ICollection<Person> People { get; set; } in Project
and
public virtual ICollection<Project> Projects { get; set; } in Person
And every time i try to add more than one User to the project i get the error in that title when calling the Save() method from DbContext
I have no idea what to do.
Thanks.
Accidentally posted the wrong class, it was the result instead of the Person, now it's correct
Update 1
public void AddProjectForUsers(IEnumerable<int> userIds, Project project)
{
foreach (var id in userIds)
{
AddProjectForUser(id,project);
}
}
public void AddProjectForUser(int userId, Project project)
{
var user = _ctx.Users.SingleOrDefault(u => u.Id == userId);
if (user != null)
{
if (!user.Projects.Contains(project))
{
user.Projects.Add(project);
}
}
}
public bool Save()
{
return _ctx.SaveChanges() > 0; // the error is thrown here
}
The 3 are from my repository class
And the call to them is
Repo.AddProjectForUsers(team, project);
Repo.Save();
Where team is an IEnumerable<int> and project is of type Project
It looks like you may need the InverseProperty data annotation attribute to do this:
namespace Prometheus.Models
{
[Table("People")]
public class Person : IPerson, INamedEntity
{
...
[InverseProperty("People")]
public virtual ICollection<Project> Projects { get; set; }
}
}
public class Project :INamedEntity
{
...
[DisplayName("Echipa proiect")]
[InverseProperty("Projects")]
public virtual ICollection<Person> People { get; set; }
}
The exception message you are receiving is very telling. Since EF is complaining that the relation is expected to be a one-to-zero-or-one, this leads me to believe that EF is confusing the People - Projects association with either the PersonInCharge - Project or ProjectManager - Project association.
One way to solve this would be to expose foreign key properties on your Project entity, which you should be doing anyway - you can get weird transient errors when you don't. Another way would be to use the fluent API (a.k.a. model builder) instead of data annotations.
I'm learning EF using Code First and I'm having a lot of trouble getting my relationships to build correctly.
A Simple Employee
public class Employee
{
[Key]
public int EmployeeId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
}
A Simple Project
public class Project
{
[Key]
public int ProjectId { get; set; }
public String ProjectNumber { get; set; }
}
The time spent on the project
public class Time
{
[Key]
public int TimeId { get; set; }
public int EmployeeID { get; set; }
public String ProjectID { get; set; }
public long? TimeSpent { get; set; }
public virtual Employee Employee { get; set; }
public virtual Project Project { get; set; }
}
I'm trying to join Employee to Time on EmployeeID and join Project to Time on ProjectID and I just don't understand how EF determines relationships. I'm trying to use Data Annotations to define the relationships. I've tried using the ForeignKey annotation to define the relationships but that has not worked either.
What I have will run, but on the Project table, a new field named Project_ProjectID is created and if I try to run a query in VS I get an error saying that the column Time_TimeID is invalid (which it is). What am I doing wrong?
You shouldn't need DataAnnotations as conventions will work for you in this case
Try the following
public class Time
{
[Key]
public int TimeId { get; set; }
public int EmployeeID { get; set; }
public int ProjectID { get; set; } //<< Changed type from string
public long? TimeSpent { get; set; }
public virtual Employee Employee { get; set; }
public virtual Project Project { get; set; }
}
public class Employee
{
[Key]
public int EmployeeId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
// Set up the other side of the relationship
public virtual ICollection<Time> Times { get; set; } // << Added
}
public class Project
{
[Key]
public int ProjectId { get; set; }
public String ProjectNumber { get; set; }
// Set up the other side of the relationship
public virtual ICollection<Time> Times { get; set; } // << Added
}
This article may help
http://msdn.microsoft.com/en-gb/data/jj679962.aspx
I'm new to ASP.NET MVC and EF hopefully this is not a silly question
When i pass model to view i'm getting this error - Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'Environment_Id'.
Model nor database table has a property by that name. Could any guide me on this?.
**Here is the Version Model Class**
public partial class Version
{
public Version()
{
this.ProfileVersions = new List<ProfileVersion>();
this.ServerInfoes = new List<ServerInfo>();
}
public int Id { get; set; }
public string Number { get; set; }
public string Tag { get; set; }
public string Owner { get; set; }
public string Approver { get; set; }
public string Description { get; set; }
public virtual ICollection<ProfileVersion> ProfileVersions { get; set; }
public virtual ICollection<ServerInfo> ServerInfoes { get; set; }
}
**Profile Version Class**
public partial class ProfileVersion
{
public ProfileVersion()
{
this.PlatformConfigurations = new List<PlatformConfiguration>();
}
public int Id { get; set; }
public int ProfileId { get; set; }
public int EnvironmentId { get; set; }
public int VersionId { get; set; }
public Nullable<bool> Locked { get; set; }
public string LockedBy { get; set; }
public string Comments { get; set; }
public Nullable<int> Active { get; set; }
public virtual Environment Environment { get; set; }
public virtual ICollection<PlatformConfiguration> PlatformConfigurations { get;
set; }
public virtual PlatformProfile PlatformProfile { get; set; }
public virtual Version Version { get; set; }
}
**ServerInfo**
public partial class ServerInfo
{
public ServerInfo()
{
this.PlatformConfigurations = new List<PlatformConfiguration>();
}
public int Id { get; set; }
public string ServerName { get; set; }
public int ProfileId { get; set; }
public int VersionId { get; set; }
public int EnvironmentId { get; set; }
public string ServerType { get; set; }
public Nullable<short> Active { get; set; }
public string Domain { get; set; }
public string Location { get; set; }
public string IP { get; set; }
public string Subnet { get; set; }
public string Gateway { get; set; }
public Nullable<int> VLan { get; set; }
public string DNS { get; set; }
public string OS { get; set; }
public string OSVersion { get; set; }
public string Func { get; set; }
public Nullable<short> IISInstalled { get; set; }
public string ADDomainController { get; set; }
public string ADOrganizationalUnit { get; set; }
public string ADGroups { get; set; }
public string LastError { get; set; }
public Nullable<System.DateTime> LastUpdate { get; set; }
public virtual Environment Environment { get; set; }
public virtual ICollection<PlatformConfiguration> PlatformConfigurations { get;
set; }
public virtual PlatformProfile PlatformProfile { get; set; }
public virtual Version Version { get; set; }
public virtual VMConfiguration VMConfiguration { get; set; }
}
**Controller Code-**
public ViewResult Index(string id )
{
var profileVerList = from ver in _context.Versions
where !(from pfv in _context.ProfileVersions
select pfv.VersionId).Contains(ver.Id)
select ver;
var bigView = new BigViewModel
{
VersionModel = profileVerList.ToList(),
};
return View(model: bigView);
}
**In the View where the exception is thrown**
#Html.DropDownList(
"SelectedVersionID",
new SelectList(
Model.VersionModel.Select(x => new { Value = x.Id, Text = x.Number}),
"Value",
"Text"
)
)
In your ProfileVersion and ServerInfo entities you have an Environment navigation property. By default, Entity Framework will try to create a database column called [Property Name]_[Referenced class PK]. In your scenario, that's Environment_Id. The problem, right now, is that you have not done a migration to have this database column created.
If I had to imagine what happened here, I'd say you first created the classes with EnvironmentId properties, migrated, then later decided to add the navigation properties, Environment to each, expecting EF to associate that with your existing EnvironmentId properties. That's where you went wrong. As I said above, EF convention is to look for a database column named Environment_Id, so if you want EF to use EnvironmentId instead, you just need to tell it so with the ForeignKey data annotation:
[ForeignKey("Environment")]
public int EnvironmentId { get; set; }
In My Case I have added My Primary Key Relationship to Same Key .. SO I have simply remove..
I realize this question is 3 years old now, but I saw a different reason for the error - both in the original question and in my own code that was pretty similar. And, in my case, I had the same error as stated above.
I had a "MY_ACTIONS" table with an ID and Name pair that I wanted to be added to a dropdown. Here's the model:
namespace TestSite.Models
{
public class MY_ACTIONS
{
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MY_ACTIONS()
{
this.o_actions = new HashSet<MY_ACTIONS>();
}
[Key]
public int action_id { get; set; }
[StringLength(100)]
public string action_name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MY_ACTIONS> o_actions { get; set; }
}
}
And to get an action to display on the dropdown it had an ID set in an int field called LASTACTION in my main table. In that model I had declared the ForeignKey relationship:
namespace TestSite.Models
{
[Table("MAIN_TABLE")]
public partial class MAIN_TABLE
{
[Key]
public int MAIN_TABLE_ID { get; set; }
public int LASTACTION { get; set; } // this would carry a number matching action_id
[ForeignKey("LASTACTION")]
public virtual MY_ACTIONS MY_ACTIONS { get; set; }
}
}
I had the error Invalid column name 'MY_ACTIONS_action_id' when loading this dropdown in my view:
#Html.DropDownList("lastaction", null, htmlAttributes: new { #class = "form-control" })
...for which I was using this ViewBag in my Controller function:
Model1 db = new Model1(); // database context
MAIN_TABLE o_main = new MAIN_TABLE();
o_main.lastaction = 2;
ViewBag.lastaction = new SelectList(db.MY_ACTIONS, "action_id", "action_name", o_main.lastaction);
If I did not have my FK relationship declared:
[ForeignKey("LASTACTION")]
public virtual MY_ACTIONS MY_ACTIONS { get; set; }
I probably also would've had the same issue. Having the representation of a virtual instance requires linking it with some physical property. This is similar to how this:
public virtual Environment Environment { get; set; }
Should be:
[ForeignKey("EnvironmentId")]
public virtual Environment Environment { get; set; }
in the ProfileVersion class, in the question, above, assuming that EnvironmentId is the Primary Key in a table called Environment (that model is not shown above).
For me, though, I already had that and I was still getting the error, so doing that still might not solve everything.
Turns out all I had to do was get rid of that ICollection<MY_ACTIONS> o_actions in the MY_ACTIONS model and the this.o_actions = new HashSet<MY_ACTIONS>(); line and it all went through fine.
There are many such lists and ICollections in play in the question above, so I would wager something is wrong with having them, as well. Start with just a plain model that represents the fields, then add in your virtual objects that represent tables linked to with foreign keys. Then you make sure your dropdown loads. Only after that should you start adding in your ICollections, HashSets, Lists<T> and other such amenities that are not actually physically part of the database - this can throw off Entity Framework into thinking it needs to do something with them that it doesn't need to do.