In EF 6 (code first), is it possible to have a navigation property set-up without enforcing a referential integrity constraint?
For example:
public class Person{
public IList<Pet> Pets { get; set; }
}
public class Pet{
public int OwnerId { get; set; }
public Person Owner { get; set; }
}
So in the example above I'd like to be able to add a Pet with an OwnerId, even if that owner does not exist in the Owners table.
Thanks
Matt
You can define the relationship using the fluent API.
modelBuilder.Entity<Pet>
.hasOptional(p => p.Owner)
.willCascadeOnDelete(false);
This will configure the relational property as optional, and will ensure that cascade delete does not take effect. You can create a Pet without an Owner, and deleting an Owner will not delete the associated Pets.
However you cannot assign Pet.OwnerId to an OwnerId that doesn't exist in the Owner table. If you truly need to have some way of tracking invalid OwnerId values, you either need to have a separate property which you manually update with an arbitrary value, or you would need to define these objects without using a navigation property, and perform your lookups manually.
It would be an exceptional situation where you would need to supply an arbitrary value for OwnerId that doesn't match the Owner table; In 99% of all cases, an optional relationship which accepts a valid OwnerId or null is all that is necessary.
The OwnerId property isn't actually necessary on the Pet object, but if it is present, it should be set to int? to be nullable.
Short Answer: Don't use EF. The whole point of Object Relational Mappers is to ensure you have valid data as well as help with retrieving/persisting it
All you really want is some sort of mapper.
Long Answer: Curious myself
Related
I have multiple projects that return the same OData entities through a API endpoint. Now i want to call all of the projects and store them in my calling projects database with entity framework.
To add them to the db the ID gets overwritten but i want to save the id that the entity has in the projects database as well. so i can still access them if need be and to check if the data isn't already in my database. Because of this i need to add another MainProjectID and projectID column to the entity.
I tried making a new class that has a reference to the entity i want to save but this used new id's for the entities. I also tried inheriting the class but this gave me key conflict issues, and generics don't work either in entity framework(i'm not saying they should). So i'm kinda at a loss right now.
I basically want to save the id as a non-key. Is there any way i can do this without writing entirely new classes and parsing them manually ?
Any help would be greatly appreciated.
We have multiple alternatives here:
In a distributed system, best way to cope with these kinds of ID clashes is to make IDs globally unique. If you can modify how IDs are generated, that would be my choice to go. You can use a UUID (or Microsoft implementation GUID) that will produce a universal unique identifier. Or if that seems like an overkill you can devise a simple mechanism that combines ID with projectID. However you should ensure that the method you will use will not produce any collisions (no two different id-projectId pair will map to same value).
This will ensure that same entity is used throughout your application and no overlaps occur if you try to put records from different sources into the same table. You only need to implement a mechanism to record which ID originated from which source. You can use a reference entity at aggregator for this purpose. You also need to disable auto increment nature of the ID column so that your global unique values are used in table.
You can use different entities for producing and aggregating applications. I don't know your application, but that seems like an OK approach to me since the aggregating application has a different idea about the entity. The aggregating application cares for which application produced the entity, that might make putting the source application identifier into the entry justifiable. Your entities will only differ in that and when you receive the OData object from API you'll need copy all other properties and put project identifier yourself.
You can use the previous solution, but you can use a derived class in order to not to repeat your object properties. This is a better design alternative. However with this method you'll have some problems with the primary key (as you've stated you had). Consider this example
public class Base {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Derived : Base {
[Key]
public int projectId {get; set; }
}
If you don't put [Key] to Derived then you'll have only ID as primary key. When you put [Key] to Derived then you'll have only projectId as primary key. You need to define a composite key and you can do this by removing the [Key] annotation from projectId and using the onModelCreating override of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Derived>().HasKey(a => new { a.ID, a.projectId })
.Property(c => c.ID).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
modelBuilder.Entity<Derived>().Property(c => c.projectId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
}
You can mix these alternatives. You can drop the primary key on ID field, and then you can insert a new Entity that will model 1-M relationship between ID's and project ID's.
I've gone through the Entity Framework code-first tutorials and I hate to say but I'm at a loss for how it works. Everything works but I want to know how because everything I know about basic database design tells me it shouldn't. I feel like I'm missing a critical link with how it's creating foreign keys and hope someone can explain it.
I initially copied and pasted a bunch of code-first designs of a simple Parent class with multiple child types. In my child types I had a reference to the virtual Parent and a ParentID reference. I noticed in the database that the value of ParentID was 0 for every single child of type A and B.
However, when loading the data in code and debugging/displaying, entity relationships were correctly maintained.
The code looks like (class names were changed to remove context of what it is):
public class Parent{
public int? ParentID {get;set;}
public virtual ChildA ChildA {get;set;}
public virtual ChildB ChildB {get;set;}
}
public class ChildA{
[Key,ForeignKey(Parent)]
public int ChildAID {get;set;}
public string Field {get;set;}
//public int ParentID {get;set;}
[ForeignKey("ParentID")]
public virtual Parent Parent {get;set;}
}
public class ChildB{
[Key,ForeignKey(Parent)]
public int ChildBID {get;set;}
public string Field {get;set;}
//public int ParentID {get;set;}
[ForeignKey("ParentID")]
public virtual Parent Parent {get;set;}
}
I commented out the ParentID to show explicitly what I was talking about. To test this out, I deleted the column in the database and yet the relationships still work correctly. There are only three tables that relate to this in the database (Parent, ChildA, and ChildB). There aren't any foreign keys, and the parent table doesn't reference the children tables and the children tables don't reference the parent table. When I load them from the DBContext, the mappings correctly load them as I saved them.
What am I missing here? Something somewhere is keeping track of this mapping but I can't tell where. And the fact that it isn't stored as a really basic foreign key in the database concerns me about performance.
What do you mean by There aren't any foreign keys, and the parent table doesn't reference the children tables and the children tables don't reference the parent table. ?
I think you were missing to notice that there are relationship between them.
It's called one to zero-or-one relationship.
Your question is a bit baffling because Entity Framework does store relationships as "really basic foreign keys", so I can't see how you've come to that conclusion.
Commenting out the ParentID to show what your talking about doesn't help much because it should actually have had no effect. That is because when you add the [ForeignKey("ParentID")] attribute to the Parent property, you are telling Entity Framework to put a foreign key in the database named "ParentID" to model the Parent relationship.
You don't need the ParentID integer property in your Entity, but it is usually easier to include foreign keys, and most examples show this.
Now, if you do include ParentID, then you don't need the [ForeignKey("ParentID")] attribute on the Parent property because EF will assume that ParentID is the foreign key for the Parent navigation property because you have followed a standard naming convention.
Also you could try this - remove both the ParentID and the [ForeignKey("ParentID"] attribute and update the database. Now you should find that EF has added a foreign key named "Parent_Id". So foreign keys used always!
The [Key,ForeignKey(Parent)] attribute on your keys looks wrong. The ID isn't a navigation property so you don't need to specify a foreign key for it, so I'm not sure what that is doing (actually, on second thoughts, this may be your problem, I think this is specifying a one to one). That Key attribute is also unnecessary because again you have followed a naming convention.
So in summary, get shot of all your attributes and follow the naming conventions until you need to do something tricky!
I have seen similar questions to this; but none quite the same; and none have helped me. I want the migration to use a different name for the column than I have for the property in my class. On built-in types, I am able to do this with [Column("newName")]. However, this doesn't work when I want a foreign key to another class.
In other words, this works just fine:
[Column("NameInDB")]
public string NameInCode { get; set; }
But this doesn't work at all:
[Column("Employee_Id")]
public virtual Employee Owner { get; set; }
In the second case, the migration still creates the column as Owner_Id; it completely ignores the Column annotation. Is there somewhere that it says that the Column annotation only works for built-in types? I couldn't find anything about that.
I know that it is possible to use the [ForeignKey] annotation to do this, but if I do, I have to have an extra property in my code that I don't want:
[ForeignKey("Employee_Id")]
public virtual Employee Owner { get; set; }
public int Employee_Id { get; set; }
I don't want to do that because the Employee_Id property is redundant in that case; I'd rather just use the Owner property. Is there a way around this, or a good reason that [Column] seems to be ignored?
public virtual Employee Owner { get; set; } is not mapped to DB, it is not represented in database. It populated by Entity Framework automatically for your convenience. Employee_Id - this is what is stored in DB. Entity Framework uses Employee_Id field to create Owner object. So, Emplyee_Id is not redundant in that case, it is mandatory and it has physical representation in DB, but Owner field is logical part of the class, managed by Entity Framework and not have it's column in DB. Makes sense?
I have two entities and there are their POCO:
public class DocumentColumn
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual long? DocumentTypeId { get; set; }
}
public class DocumentType {
public virtual long Id { get; set; }
public virtual string Name { get; set; }
}
There is a relation between those two entities. In the db the relation called:FK_T_DOCUMENT_COLUMN_T_DOCUMENT_TYPE.
When I do:
DocumentColumns.Where(x => x.DocumentTypeId == documentTypeId).ToList();
I get the exception:
{"Metadata information for the relationship 'MyModel.FK_T_DOCUMENT_COLUMN_T_DOCUMENT_TYPE' could not be retrieved. If mapping attributes are used, make sure that the EdmRelationshipAttribute for the relationship has been defined in the assembly. When using convention-based mapping, metadata information for relationships between detached entities cannot be determined.\r\nParameter name: relationshipName"}
I tryed to remove the relationship and the DocumentColumn table and reload them but the code still throws the exception.
Whet does this exception means and how can I solve it?
EDIT:
The exception happens also If I do DocumentColumns.ToList();
(Presuming you are talking about Code First ....)
There is no information in either class to let CF know that there is a relationship between them. It doesn't matter that the database has the info. Entity Framework needs to have a clue about the relationship. You provide only a property with an integer. CF cannot infer a relationship. You must have something in one class or another that provides type or another. This is not a database. It's a data model. Very different things.
But that's not all. I'm guessing that this is a one to many relationship. You could either put a List property into the Document class or a Document property in the DocumentColumn class. If you only do the latter, CF and EF will NOT know about the 1:. It will presume a 1:1 (that is if you leave DocumentId integer in there, otherwise it will presume a 1:0..1). However, I think you could get away with this and then just configure the multiplicity (1:) in fluent API.
UPDATE...reading your question again, I think you are using an EDMX and designer not code first. What are you using to create your POCO classes? Are you doing code gen from the EDMX or just writing the classes. I still think the lack of a navigation property in at least ONE of the types might be the cause of the problem. The ERROR message does not suggest that...I'm only coming to this conclusion by looking at the classes and inferring my understanding of how EF works with the metadata. I could be barking up the wrong tree. FWIW, I have asked the team if they are familiar with this exception and can provide some idea of what pattern would create it. It's pretty bizarre. :)
It seems odd to me that you are using EF with a defined relationship and you are not using the related property. Can you not do:
DocumentColumns.Where(x=>x.DocumentType.Id == documentTypeId).ToList();
This is what I would expect to see in this instance.
I'm working on a new entities model creation.
I identified several repeated properties(columns) which naturally needs to be inherited, like Id, CreatedBy, CreatedAt etc.
Now, I created a base class for this issue and it all works OK until I'm defining custom types properties in the base class, for example:
public User CreatedBy{get;set;}
when running the application an EngineExecutionException is thrown.
If I'm omitting the public User CreatedBy{get; set;} and leave only the int, string, DateTime properties, it's fine and I can see it reflected in the DB creation.
What is my mistake?
I think your property should be virtual, which means that this property is a foreign key.
If UserID is a key to the User table (say an int), then you would have
public int UserID { get; set; }
public virtual User CreatedBy { get; set; }
The UserID property is a direct reference to the field in the table. The CreatedBy property is a reference to the User object that would be a representation of the User table. As mentioned elsewhere, the virtual keyword is used to make the User object lazy-load.
The UserID property matches the primary key on the User table and object. There are ways to match them up if they're not named the same, but it's easiest if they are. If you can give more info for CreatedBy such as type, how it's represented in the database, etc. I can give a more specific answer to your setup.
*edited for formatting.