I admit using MVC3/EF4 has been a breeze and the context:models work better then I'd hoped (though I'm always leary of constructs/frameworks that make things happen behind some curtain, but hey I'm old school grey beard), and all went well until I hit this issue.
I've seen many postings related to this issue but don't know how to solve it in my case. Each table(entity) has an employeeID field and it can (and usually is) a different employee for most records in each table. Apparently only one table in the DbContext can have a 'virtual employee employee' (or whatever I choose to name it) defined or else I get the dreaded "cyclical reference error". Ideally I'd like all three tables to have it so I can easily access the employees name. I may have to override the OnModelCreating() but that's (fluent API) totally out of my league. Any EF gurus out there????
Models:
class meeting{
int meetingID; //key
string description {get;set;}
int employeeID {get;set;} // who scheduled
public virtual employee employee {get;set;}
public virtual ICollection<agenda> agendas {get;set;}
}
class agenda{
int agendaID {get;set;} // key
int employeeID {get;set;} // initiator
public virtual employee employee {get;set;}
public virtual ICollection<actionItem> actionItems {get;set;}
}
class actionItem{
int actioItemID {get;set;} //key
string description {get;set;}
int employeeID {get;set;} // action item lead
public virtual employee employee {get;set;}
}
class employee{
int employeeID {get;set;}//key
string name {get;set;}
}
context:
public class meetings:DbContext{
public DbSet<meeting> meetings {get;set;}
public DbSet<agenda> Agendas
public DbSet<actionItem> actionItems{get;set;}
}
I assume you're getting this error on serialization of your models. Most people use a second set of models to abstract from the EF models, but if you want to stick with those, use the approach from this answer to get rid of your cyclical reference problem:
EF 4.1 - Code First - JSON Circular Reference Serialization Error
Related
I have a quick question and looking for the best way to do this, whether EF has the capability or not, am not sure? I am using EntityFramework 6.3.
I have the following parent-child scenario,
public class Application{
[Key]
public int ApplicationId {get;set;}
public string Name {get;set;}
public string Status {get;set;}
public virtual List<Document> Documents {get;set;}
}
public class Document{
[Key]
public int DocumentId {get;set;}
[Index("IX_ApplicationDocument", 1, IsUnique = true)]
public string DocumentType {get;set;}
[Index("IX_ApplicationDocument", 1, IsUnique = true)]
public string Name {get;set;}
public int ApplicationId {get;set;}
[ForeignKey("ApplicationId")]
public Application Application {get;set;}
}
So an application is made to a department, and stored in the database, each application has a status and when submitted, status of pending, because various validation has to occur before it is approved. When an application is rejected, the submitter has to make a new application (please note I used a minimalistic example than what it actually is), however, the applicant may submit the same documents again. The problem is, this already exist in the system and can not be duplicated. As you can see, the second time they attempt to submit it will throw a constraint exception. How can I overcome this using EF, is there a way to create a constraint based on the parent's status, or is this something that can only be done programmatically?
Dont know if its helps you in your case or not, but check this out
Assuming your entity is defined as
public class Entity
{
public int Id { get; set; }
public int Parent { get; set; }
public int Child { get; set; }
}
Following fluent API code will create index as you desire:
modelBuilder.Entity<Entity>().HasIndex(p => new {p.Parent, p.Child})
.HasFilter("isdeleted = 0")
.HasName("unq_t_parent_child");
SQL generated
CREATE INDEX [unq_t_parent_child] ON [Entity] ([Parent], [Child]) WHERE isdeleted = 0;
HasIndex defines index over properties in the table Entity
HasFilter allows you to set a filter for your index. This value is sql so you need to make sure you are writing correct sql syntax.
HasName configures the name of the index.
(If you map Entity to table t & the properties to their column names, migrations will create exactly same sql as you want.)
Also a check constraint is different from unique index. If you are looking to add check constraint then you need to use migrationBuilder.Sql in your migration file.
So unfortunately I have searched around and there is no solution for this for EF 6. The best way I can do this was following the guidance of the following article, where you manually add the Filtered Index in your migration after table creation.
Blog
Trying to do something that I believe should be simple.
Customer
------
CustId
BillingId
ShippingId
...other customerInfo
Address
------
Id
...other addressinfo
I have the corresponding POCOs
public class Customer {
[Key]
public int CustId {get;set;}
[Column("BillingId")]
[ForeignKey("BillingAddress")
public int? BillingId {get;set;}
public virtual Address BillingAddress{get;set;}
[Column("ShippingId")]
[ForeignKey("ShippingAddress")
public int? ShippingId {get;set;}
public virtual Address ShippingAddress{get;set;}
...others...
}
public class Address {
[Key]
public int AddressId{get;set}
... others...
}
The BillingId and ShippingId are nullable because the customer may or may not have set an address yet. I'd assume with EF that if the values are null, the ShippingAddress and BillingAddress values should also be null. When I take a look at the object I'm getting back when running the application, all of the data on my Customer object is set but on the ShippingAddress/BillingAddress fields, in debug mode when I inspect the object, I get the below error:
BillingAddress = '((System.Data.Entity.DynamicProxies.CustomerD_E865AA67CAAA399D4F193A2439775301DFD2F05115619BC048699B20BF6F7B11)details).BillingAddress'
threw an exception of type 'System.InvalidOperationException'
A same error appears for the ShippingAddress field. The application actually continues to run, the exception only gets thrown when inspecting in debug more. For the particular inspected object, the ShippingId and BillingId are populated correctly.
Not sure why this is happening given my setup.
one possible reason is: your repository is registered as singleton in DI configure.
another possible reason: add ForeignKey and InverseProperty to navigation properties
public class Customer {
[Key]
public int CustId {get;set;}
//[Column("BillingId")]//not necessary if real column is same as property
public int? BillingId {get;set;}
[ForeignKey("BillingId")]
[InverseProperty("Customer")]
public Address BillingAddress{get;set;} //no need to be virtual
//[Column("ShippingId")]
public int? ShippingId {get;set;}
[ForeignKey("ShippingId")]
[InverseProperty("Customer")]//InverseProperty: Specifies the inverse of a navigation property that represents the other end of the same relationship.
public Address ShippingAddress{get;set;} //no need to be virtual
...others...
}
try Scaffold-DbContext in a new project to validate your pocos:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#scaffold-dbcontext
Scaffold-DbContext "your connection string" -DataAnnotations -OutputDir "Models" -Force
I have these classes:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
}
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}
I got an error saying: Introducting Foreign Key Constraint may cause cycles or multiple cascade paths.
DbEntity is an abstract class containing the Primary Key. [Key] public Guid Id {get;set;}
So, I changed Application to:
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
[ForeignKey("MinimumSystemRequirements")]
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
[ForeignKey("RecommendedSystemRequirements")]
public Guid RecommendedSystemRequirementsId {get;set;}
}
So my question is why doesn't this work? I even tried putting a reference to Application in SystemRequirements, that didn't work?
Please don't trawl this post for spelling mistakes. My code is fine on VS, I copy and paste rather type out.
SystemRequirements probably needs to have the MinimumSystemRequirementsID and RecommendedSystemRequirmentsID fields. My understanding of EF FK relationships is it looks at the model for the value of the Element you are calling a FK and then uses that for linking.
Try this:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}
You have to tell EF that (at least) one of the associations between Application and SystemRequirements has no cascading delete, for instance:
modelBuilder.Entity<Application>().HasRequired(a => a.MinimumSystemRequirements)
.WithMany().HasForeignKey(a => a.MinimumSystemRequirementsId)
.WillCascadeOnDelete(false);
(In the context's overload of OnModelCreating).
Heyyyyy good news. I figured out the issue!
I remember back in the day (couple of months ago) I was told that in order to enable Cascade On Delete using Attributes in Code First you must do this:
public Guid CascadeOnId {get;set;}
public virtual Cascade CascadeOn {get;set;}
However before I knew that I was always using this:
public virtual Cascade CascadeOff {get;set;}
The reason I'm getting the cyclic delete, is because the first example will delete the HDD if it exists, but the HDD must always exist for the second instance of it, example:
public Guid FirstCascadeId {get;set;}
public virtual Cascade FirstCascade {get;set;}
public Guid SecondCascadeId {get;set;}
public virtual Cascade SecondCascade {get;set;}
So, entity framework is worrying that if you delete a cascade entity, it must delete it's associated parent, in deleting a parent you must delete all associated cascades. Which is where the cycle begins. I hope I'm making some sort of sense.
In order to turn Cascade On Delete off, you mustn't specify a Guid Id relating to that virtual. So In order to make the above work, you use:
public virtual Cascade FirstCascade {get;set;}
public virtual Cascade SecondCascade {get;set;}
You can turn on the first Cascade On Delete if you want, just make sure one of them hasn't got it and it works fine :)
I found the answer by using Model First, and then getting a massive glowing bulb above my head :P
I am trying to model what I think is quite a basic scenario, but I can't get my head around the correct way to define it in EF Code First 5.
I have three classes:
Employee
Postal Address
Contact Telephone Number
The rules are thus:
A Postal Address can be standalone
A Contact Telephone Number can be standalone
Optionally, an Employee can have a "Home" Postal Address
An Employee can have zero or more Contact Telephone Numbers
If I model this in SQL, I end up with four tables;
PostalAddress
ContactTelephoneNumber
Employee
EmployeeContactTelephoneNumber (bridge table)
By looking through the answers here on SO, I can produce something similar to my SQL model in Code First with the exception that I have to have an Employee navigation property on PostalAddress and an Employees navigation property on ContactTelephoneNumber. This goes against my business rules, because neither PostalAddress nor ContactTelephoneNumber will always be refered to be an Employee. For example, I could later add a Premises class which would also have a PostalAddress.
By way of code examples, what I have now resembles the following:
public class Employee
{
public int EmployeeID {get;set;}
public virtual PostalAddress? HomeAddress {get;set;}
}
public class PostalAddress
{
public int PostalAddressID {get;set;}
public string Address {get;set;} // It's not actually a string - this is for brevity!
}
public class ContactTelephoneNumber
{
public int ContactTelephoneNumberID {get;set;}
public string TelephoneNumber {get;set;} // It's not actually a string - this is for brevity!
}
I'm trying to define my relationships using the Fluent API rather than Annotations, to ensure my presentation layer remains unaware of Entity Framework when consuming these classes. My mapping currently resembles the following - is this "correct"?
public class EmployeeMap : EntityTypeConfiguration<Employee>
{
public EmployeeMap()
{
this.HasOptional(e => e.HomeAddress).WithOptionalDependent(p => p.Value).Map(m => m.MapKey("PostalAddressID"));
}
}
This doesn't compile; I get:
The type 'PostalAddress?' must be a reference type in order to use it as parameter 'TTargetEntity' in the generic type or method 'System.Data.Entity.ModelConfiguration.EntityTypeConfiguration.HasOptional(System.Linq.Expressions.Expression>)'
Additionally, I don't like that I'm passing "PostalAddressID" as a string constant into the call to MapKey().
Please could someone highlight the error of my ways? I've been searching for the past 3 hours to no avail!
Edit: I should mention that once I understand this part I'll try and address the Employee>ContactTelephoneNumbers situation separately.
You have declared HomeAddress as a value type, specifically as a Nullable<T>.
Remove the ? from the declaration (reference types allow null, and therefore do not need to be wrapped in the Nullable struct).
public virtual PostalAddress HomeAddress {get;set;}
To specify the mapping, I think you can use this:
this.HasOptional( e => e.HomeAddress ).WithMany().HasForeignKey( e => e.HomeAddressID );
You will also need to add the HomeAddressID property to the Employee class.
My model looks like this
public Class Address
{
public int Id {get;set;}
/*Props here*/
}
public Class Person
{
public int Id {get;set;}
public String Name {get;set;}
[Required]
public Address Address{get;set;}
/*More props*/
}
Now suppose i have created a person with proper address, in future when i try to update person like this
var person= db.Persons.FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
It gives error saying Address is required.
So to avoid this iam including Address property too while loading Person Entity
var person= db.Persons.Include(p=>p.Address).FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
Is there any way i can update person Without including Address.
It's the model validation of DbContext which complains apparently. So, one solution would be to switch off this validation:
dbContext.Configuration.ValidateOnSaveEnabled = false;
The other option is to introduce a foreign key property:
public class Person
{
public int Id {get;set;}
public String Name {get;set;}
public int AddressId {get;set;}
public Address Address {get;set;}
/*More props*/
}
You can omit the [Required] attribute here because EF will detect the relationship as required by convention (due to the non-nullable FK property). This works also with enabled validation.
The behaviour is a bit confusing since EF doesn't send a change of the FK column to the database, so there is not really a constraint violation and the Update command executes fine. I guess that the validation just checks the state of the model in memory (invalid, because Address is null) and not the state the model would have in the database when SaveChanges did execute (valid, because FK is correctly set).
If you want the address to be automatically loaded by EF 4.1 you have to make the Address-porperty virtual:
public virtual Address Address{get;set;}
EF will then lazy-load the address when needed.