I have two classes:
public class Report
{
public int IdReport {get;set;}
public int IdModel {get;set;}
public Model MyModel {get;set;}
}
public class Model
{
public int IdModel {get;set;}
public Report MyReport {get;set}
}
I have this configuration class:
public class ReportConfiguration : EntityTypeConfiguration<Report>
{
HasOptional(c => c.Model).WithOptionalPrincipal(d => d.Report)....
}
This is where I got stuck, how can get the HasForeignKey(c => c.IdModel) configuration
I need this because we have a form using binding source, and a report can have or not a model(since we make dynamic reports).
I dont wanna create a fake foreign key, like creating a property and on report insert, I set this property value with primary key from the model. This is a way I found to fill the bindingsource with correctly value to be bounded with the combobox value edit.
I see that the navigation propertu MyModel.IdModel can provide this funcionality, but does binding source can accomplish this approach ?
I'm not sure that this is exactly what you need but this will create a nullable foreign key relationship from Report to Model and vice versa:
public class Report
{
public int ReportId { get; set; }
public int? ModelId { get; set; }
public Model Model { get; set; }
}
public class Model
{
public int ModelId {get; set;}
public Report Report { get; set; }
public int ReportId { get; set; }
}
public class ReportConfiguration : EntityTypeConfiguration<Report>
{
public ReportConfiguration()
{
this.ToTable("Report");
this.HasOptional(m => m.Model).WithMany().HasForeignKey(m => m.ModelId).WillCascadeOnDelete(false);
}
}
Hope it helps.
With your mapping, Report is the principal entity, which means that Model has the foreign key. So you wouldn't be able to set a ModelId in Report anyway. You have to set the Model in stead.
Technically, this is not a big deal. You can populate a combobox (or a bindingsource) with Models, not set a ValueMember and bind the SelectedItem.
Performance-wise it probably doesn't really matter either, unless Model has very wide records and you intended to only select its Id and Name properties. Now you have to fetch the full objects from the database.
I've always wondered why the 1-1 API doesn't allow defining a foreign key association (i.e. a reference and primitive foreign key pair) but forces you to work with an independent association (reference only). So far, the reason is not clear to me. It looks to me that, in this case, Model could have had a ReportId exposed in the class model.
Related
I have a problem with Entity Framework, I think I have a problem defining the one to many mapping, but I can't seem to find it.
When I try to save my changes and add my info to the database I get the following error:
Invalid object name 'Results1'
With Results being the name of one of my tables, for some reasons it's adding the 1 at the end.
I have a Results table where I need to store an Id and several other information regarding results of a test, then I have a Reasons table where I need to store several reasons for each Result.Id.
This is how I defined the Reasons class:
public class Reasons
{
public int Id { get; set; }
public int ResultId { get; set; }
public string Description { get; set; }
public Results Results { get; set; }
}
Then I have the Results class:
public class Results
{
public int Id { get; set; }
//Other properties
public ICollection<Reasons> Reasons { get; set; }
}
This is my Reasons configuration:
configurator.
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
configurator
.HasRequired<Results>(s => s.Results)
.WithMany(g => g.Reasons)
.HasForeignKey<int>(s => s.IdResults);
Solved: the only problem was that the "Reasons" and the "Results" class did not completely match the data model. Once that was repaired the problem disappeared.
Rename your foreign key in Reasons class from ResultId to ResultsId or explicitly use [ForeignKey("Results")] attribute above it. From here:
EF makes a property as foreign key property when its name matches with
the primary key property of a related entity
In your case it should be:
// Foreign key for Results
public int ResultsId { get; set; }
// Navigation property
public Results Results { get; set; }
Remove your configuration instructions. There is no need to use fluent api to configure your database scheme since entity framework will do it by self. Here are examples how to configure one to many relations via code first approach
As was answered in comments it is a good practice to use singular name for a model in order to use plural form for navigational properties.
Note: I asked a similar question yesterday, but I've since moved past that problem into another issue. Although it's very closely related, I think it's best expressed in a separate question.
I have three models: Account, AccountType, and Person. I want to make a single form page, through which a new Account, with a specific AccountType, and with specific Person information could be POSTed to the database.
public class AccountType
{
[Key]
public int AccountTypeID { get; set; }
[Required]
public string AccountTypeName { get; set; }
}
public class Person
{
[Key]
public int PersonID { get; set; }
// Bunch of properties not relevant to the question here...
}
public class Account
{
[Key]
public int AccountID { get; set; }
[ForeignKey("AccountType")]
public int AccountTypeID { get; set; }
[ForeignKey("Person")]
public int PersonID { get; set; }
// A few properties here...
public virtual AccountType AccountType { get; set; }
public virtual Person Person { get; set; }
}
Since creating a new account requires me to insert into the account as well as the person table, I created a view model for both of these models:
public class Register
{
public Account Account { get; set; }
public Person Person { get; set; }
}
In the Register view, I simply bound the properties from the Account and Person models to form fields. I also used a ViewBag to display a list of AccountTypes in a dropdown.
The part that I don't understand is in the POST controller:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Register(Register Register)
{
if (ModelState.IsValid) {
_db.Accounts.Add(Register.Account);
_db.SaveChanges();
return View(Register);
}
// do something else
}
The ModelState check passes successfully, after having commented out the Nullable setting in the project file. However, Register.Account has null properties:
All the values that I bound in the Register view get set correctly, but I did not bind the navigation properties (Register.Account.AccountType and Register.Account.Person) to anything, since I did not know what to do with them.
Now, I can't insert into the database with the above code, because I get a Person foreign key constraint error. It seems that Register.Account cannot have null values for its Person or AccountType navigation properties. Apparently, they must be set (or, at least, the Person property must be).
I know that I can set these navigation properties manually in the controller. For Person, I can write something like this before saving to the DB: Register.Account.Person = Register.Person, and I can likewise come up with something for AccountTypes to give it its proper value. I've tested this, and it does insert into the database.
But, this doesn't strike me as the right approach. It seems to me that there must be a better, more proper way of clarifying the model or table relationships to .NET before inserting into the database.
Does anybody know a better way?
P.S.: I'm using .NET 6.
Per Jeremy's suggestion, I solved this problem by creating a new View Model, which only included the properties that I needed to bind, and omitting any navigation properties that weren't necessary to insert into the database successfully.
I'm preparing a project's data structure (code-first) in an ASP .NET Core 2 application, with the help of Entity Framework. This specific relationship I have no experience with: the user has to be able to choose diseases with checkboxes, and we have similar choices: cancer type, dietary, etc..
I have more than two tables like the ones on the picture, which will be referred from the UserKitProperties table. This table should work like a connector table, connects the user entity with other entities.
userid1 | cancertypeid1
userid2 | dietaryid1
userid1 | cancertypeid2
userid3 | dietaryid1
How should this be specified in the code, to support this relationship? I was thinking on doing a base class and maybe refer to that id. And this is the connector class..
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
// this should be used for cancer type, dietary, etc..
public long PropertyID { get; set; }
/* Instead of using two classes' ids, maybe call the base class' id
[ForeignKey("PropertyID")]
public CancerType CancerTypes { get; set; }
[ForeignKey("PropertyID")]
public Dietary Dietaries { get; set; } */
}
Thank you in advance for your suggestions! :)
The following should work:
public class Property
{
public long PropertyId { get; set; }
}
public class CancerType : Property
{
// Your code
}
public class Dietary : Property
{
// Your code
}
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long PropertyID { get; set; }
[ForeignKey("PropertyID")]
public Property Property { get; set; }
}
But as this MS doc mentions, setting up such inheritence will use a special Discriminator
column in the base class table, to represent what specific type is stored in a row.
I personally would resort to having nullable fields instead in order to not add more complexity. This doesn't enforce, however, that PatientProperties only has one property, which is a considerable minus:
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long? CancerTypeID { get; set; }
[ForeignKey("CancerTypeID")]
public CancerType CancerType { get; set; }
public long? DietaryID { get; set; }
[ForeignKey("DietaryID")]
public Dietary Dietary { get; set; }
}
Instead of thinking about the database layout first, you should think about how you would represent this relationship in code. After all, you are doing a code-first approach.
There are basically two choices you could choose: Either the patient has multiple properties, one for each property type, or there is just a single collection for all properties:
public class Patient
{
// …
// option 1
public CancerType CancerType { get; set; }
public Dietary Dietary { get; set; }
public OtherProperty OtherProperty { get; set; }
// option 2
public IList<PatientProperty> Properties { get; set; }
}
Both of these options have their advantages and disadvantages. While option 1 is very explicit and enforces a single value for every type, it also requires you to have a (class) property for every (patient) property. So if you extend your model later, you will have to adjust your patient model.
Option 2 has the benefit that it can just collect everything. So you can just add properties to your patient without having to modify the model later if you introduce new properties. In addition, it would also directly support multiple selections for a single kind. On the downside, it does not verify anything on its own, so you need business logic to actually enforce your rules.
Moving onto the database, for option 2 you obviously need a link table since that is a many-to-many relationship now. Since you only have a link to the base type PatientProperty but you actually want to talk about the concrete type, you will need some kind of discriminator. Discriminators are basically just a notation to additionally store the kind of object in the database.
When storing data with inheritance, what is commonly done is “table-per-hierarchy”. That means that all types within the hierarchy of the PatientProperty base type will share the same table. A discriminator column is used to specify the type, and additional properties that some property types may have are implemented with nullable columns. This setup works out of the box with Entity Framework and is described in this chapter in the documentation.
The other approach, “table-per-type” is not supported in EF Core, so if you wanted to follow that, you would have to implement it yourself. But in your case, where the property types are mostly very similar, I would actually argue against that and actually keep them in the same table.
For option 1, as long as you only have a single property of each kind assigned to the patient, things are a bit simpler. Since you don’t have many-to-many there, you don’t actually need a link table. You just need to store the id for each linked property type in the patient model, as shown in the above UML. Doing that, you can also keep the property types as separate types that do not share a single table in the database.
I currently have an EF entity with a navigation properly, something like:
public class Person
{
public int Id { get;set;};
public string Name { get;set;};
public virtual Office WorkingAt { get;set;}
}
public class Office
{
public int Id {get;set;}
public string Name { get;set;}
public string Address {get;set;}
}
I'd like to change from using a "Navigation Property" for WorkingAt to a simple reference to the Id of the Office object.
How do I go about doing this? I'm not clear on what the migration path should be.
I already have data in my database, and the Person table contains an Office_Id column which I'd like to keep using.
You don't need to represent the field as a foreign key in your code. Since you don't have a navigation property, it's simply a column on the Person table as far as EF is concerned. You're not using a navigation property, whether it's a foreign key or not is irrelevant, as you can no longer use it to automatically join to the Office table.
Since it's already using Office_Id as your foreign key in the database, you simply need to do this:
public class Person
{
public int Id { get;set;}
public string Name { get;set;}
public int Office_Id { get; set; }
}
Then run Add-Migration. It'll probably try to remove the foreign key in the Up method (and add it back in the Down method). Remove both these lines referring to foreign keys. Ideally, your Up and Down methods should be empty.
As discussed here with Entity Framework Code First you need to have at least one navigation property for your relationship to work.
But if you'd like to keep your navigation property (for the purposes of avoiding dealing with migrations) and just add a foreign key to your model, than you can do it using the ForeignKey DataAnnotation. This way you explicitly tell Entity Framework of your intentions.
public class Person
{
public int Id { get;set;}
public string Name { get;set;}
[ForeignKey("Office")]
public int OfficeId { get; set; }
public virtual Office WorkingAt { get;set;}
}
Another way one can achieve this is adding the ForeignKey annotation to the navigation property and tell which property will act as the Foreign Key:
public class Person
{
public int Id { get;set;}
public string Name { get;set;}
public int OfficeId { get; set; }
[ForeignKey("OfficeId")]
public virtual Office WorkingAt { get;set;}
}
I am in a strange situation right now, I have two entities:
public class Proyect
{
int id;
List<int> stages;
}
public class Stage
{
//PK, not FK to proyect
int id;
}
I know that this is not the best way to model this kind of relation (n->1)
but it was done this way and we can't change it.
Does someone know, how do I relate this entities (notation or overriding onModelCreation)?
We are using c#, ET4, VS2012, WS8.
I like to use Data Annotations for simple relationships. You must specify your key field on the Proyect table, and your foreign key on the Stage table. In the Proyect table, you should have a list of Stages, not ints, since you are relating to the Stage object. You can use the virtual keyword to use lazyloading on your related entities.
If you really need a list of type int, containing your stage Ids, just use an unmapped property.
public class Proyect{
[Key]
public int id { get; set;}
public virtual List<Stage> stages { get; set;}
[NotMapped]
public virtual List<int> stageIds {
get {
return stages == null ? null : stages.Select(t => t.id).ToList();
}
}
}
public class Stage{
public int id { get; set;}
[ForeignKey("id")]
public virtual Proyect Proyect { get; set;}
}