I am using Entity Framework code first.
I have multiple classes that required an audit trail (e.g. Car, Van).
When a change is made to an instance of this class, the audit trial is updated.
These classes all inherit from a parent (Vehicle) and they all use a GUID as an ID.
My Audit Trail class has a reference to this GUID and an audit message.
How do I configure my domain objects so that when I delete a Car, all of the corresponding Audit Trail items are deleted?
Is there a way to do this in the domain model, do I need to configure this elsewhere, or should I just be cleaning up the Audit Trail repository after every delete operation?
public class Car : Vehicle
{
public string CarProperty { get; set; }
}
public class Vehicle
{
public Guid Id { get; set; } = Guid.NewGuid();
public string ItemName { get; set; }
}
public class AuditTrail
{
public Guid Id { get; set; } = Guid.NewGuid();
public string AuditNote { get; set; }
public Guid VehicleId { get; set; }
}
You have two options:
You can keep you existing AuditTrail class where VehicleId points to either a Car or a Van etc. but without any Foreign key constraint since it will be able to point towards multiple tables in your data base (you will probably want to record which table the AuditTrail is for). This is tidy in that you won't have too many fields, but means that you will need to write code to delete AuditTrails when you delete the corresponding Vehicle.
You can create a separate field for each Table which relates to the AuditTrail (CarId, VanId etc.) and then create a Foreign Key constraint for each relationship with a cascade delete rule which will automatically delete the AuditTrail with the corresponding vehicle. This will mean adding a new field and constraint for each new Vehicle Table you add and creating code to handle all the different types when creating the AuditTrail.
public class Car : Vehicle
{
public string CarProperty { get; set; }
public IList<AuditTrail> AuditTrails { get; set; }
}
public class Vehicle
{
public Guid Id { get; set; } = Guid.NewGuid();
public string ItemName { get; set; }
}
public class AuditTrail
{
public Guid Id { get; set; } = Guid.NewGuid();
public string AuditNote { get; set; }
[ForeignKey(nameof(CarId))]
public Car Car { get; set; }
public Guid? CarId { get; set; }
}
Related
This is my code:
namespace MyProject.Models.Database
{
public class Recipe
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; }
public string? Description { get; set; }
public string? Picture { get; set; }
public int Worktime { get; set; }
public int? Cooktime { get; set; }
public int Difficulty { get; set; }
public int Portions { get; set; }
public List<Ingredient> Ingredients { get; set; }
}
public class Ingredient
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid IngredientId { get; set; }
public int Qty { get; set; }
public string QtyUnit { get; set; }
}
}
I want the class "Recipe" to include many elements of type "Ingredient". Ive read stuff about One-to-One and Many-To-Many but i just dont get it...
any Ideas?
thx
One recipe can consist of many ingredients, one ingredient can also be in many recipes. This is a many-to-many relationship.
What you need to do is create a new class that contains Id, RecipeId, IngredientId.Name that class something like RecipeIngredient. When you are creating a DbSet<RecipeIngredient> in your db context, name your table RecipesIngredients.
What should be the data types of the properties in RecipeIngredient?
The Id property will be the primary key, you can decide the data type.
RecipeId will be a foreign key for the Recipe, so it needs the same data type as the primary key of the Recipe (in your case Guid).
IngredientId will be the foreign key for the Ingredient, so the data type will again be Guid in your case.
Note that instead of putting Id in your RecipeIngredient, you can create a composite key instead.
When should you do that? -> here
I suggest you learn about the different relationships and how to apply them using C# and Entity Framework Core -> here
Good luck on your learning journey! When you don't feel you understand a topic, don't worry and don't get discouraged, you just need more experience. Keep up the good work :)
Considering the documentation here, you can define foreign key relationships in your pocos like the given example:
public class Customer
{
[References(typeof(CustomerAddress))]
public int PrimaryAddressId { get; set; }
[Reference]
public CustomerAddress PrimaryAddress { get; set; }
}
This is fine, as there's a 1:1 relationship here. However, I have a 1:Many relationship I need to define, and the relationship is actually defined in the child object, not the parent object.
So, let's say I have these POCOs:
public class Customer
{
[PrimaryKey]
public int CustomerId { get; set; }
public List<CustomerAddress> CustomerAddresses { get; set; }
}
public class CustomerAddress
{
[PrimaryKey]
public int CustomerAddressId{ get; set; }
public int CustomerId { get; set; }
}
How can I have ORMLite eager load the CustomerAddresses property in the Customer POCO?
You have to call Db.LoadSelect<Customer>() method and your customer(s) will retrieve CustomerAddresses (you need to add [Reference] attribute on top of your CustomerAddresses property).
I have a issue with updating value on a foreign key which is inheriting from another class. I am working with detached object in Entity Framework, so I'm using graphdiff to handle it.
I've simplified the code to make it easier to read
Project class:
public class Project
{
public Guid Id { get; set; }
public virtual List<Activity> Activities { get; set; }
}
Activity class:
public class Activity
{
public Guid Id { get; set; }
public String Name { get; set; }
public virtual Company Company { get; set; }
}
SurfingActivity class:
public class SurfingActivity : Activity
{
public String Comment { get; set; }
public virtual Weather Weather { get; set; }
}
The update context using graphdiff:
public void UpdateActivity(Project project)
{
this.UpdateGraph(project, map => map
.OwnedCollection(p => p.Activities, with => with
.AssociatedEntity(a => a.Company)
)
);
}
I'm only able to associate the Company property in Activity but not the Weather of SurfingActivity. When I pass along values for SurfingActivity does Id, Name, Company and Comment get saved in the Activity table and the SurfingActivity table but not weather. Does anyone have any suggestion how to solve this without having to create a new property on Project that contains a list with surfingActivity
I asked andypelzer in GraphDiff and this is currently not supported. My work around, which would work in this case (but not if the entities becomes even more nested) is to add the foreign key property as datatype it is and data annotation ForeignKey to the foreign key with the entity, like this:
public System.Guid? Weather_Id { get; set; }
[ForeignKey("Weather_Id")]
public virtual Weather Weather { get; set; }
Link to question: https://github.com/refactorthis/GraphDiff/issues/112
I am new to Entity Framework so I don't know much about it. Currently I am working on My College Project, in that Project I came across a problem where I have two foreign keys refers to the Same column in another table. how can I handle this situation.
Is it necessary to create Navigation Property for Every Foreign key. And if I create another Navigaton property for ContactId then it is necessary to create another Navigation Property in User class like:
public virtual ICollection<BlockedUser> SomePropertyName { get; set; }
please tell me the best way to overcome this problem. I am using Entity Framework 6.
Here are My Model Classes:
public class BlockedUser
{
// User Foreign Key
public int UserId { get; set; } // Composite Primary Key
// User Foreign key
public int ContactId { get; set; } // Composite Primary Key
// User Navigation Property
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; } // Primary key
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
// BlockedUser Navigation Property
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
}
Is it necessary to create Navigation Property for Every Foreign key?
Yes, or more precisely: You need at least one navigation property for every relationship. "At least one" means that you can decide which of the two entities you want to add the navigation property to. It normally depends on the most common use cases in your application if you often want to navigate from entity A to entity B or the other way around. If you want, you can add the navigation properties to both entities but you don't need to.
In your model you apparently have two (one-to-many) relationships. If you want to expose navigation properties in both entities you would need four navigation property and - important! - you have to define which navigation properties form a pair for a relationship (see the [InverseProperty] attribute in the following code snippet).
With data annotations it would like this:
public class BlockedUser
{
[Key, ForeignKey("User"), Column(Order = 1)]
public int UserId { get; set; }
[Key, ForeignKey("Contact"), Column(Order = 2)]
public int ContactId { get; set; }
[InverseProperty("BlockedUsers")]
public virtual User User { get; set; }
[InverseProperty("BlockedContacts")]
public virtual User Contact { get; set; }
}
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
public virtual ICollection<BlockedUser> BlockedContacts { get; set; }
}
If you don't want the BlockedContacts collection you can probably just remove it and the [InverseProperty("BlockedContacts")] attribute from the Contact navigation property as well.
You could use attribute ForeignKey to solve your problem. ForeignKey is used to pair navigation property and foreign key property.There is no difference between FK data annotation with Foreign Key property and FK with Navigation Properties. However, the following code will create two foreign keys with different name.
public class BlockedUser
{
// User Foreign Key
[ForeignKey("UserId")]
public int UserId { get; set; } // Composite Primary Key
// User Foreign key
[ForeignKey("BlockedUser_User")]
public int ContactId { get; set; } // Composite Primary Key
// User Navigation Property
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; } // Primary key
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
// BlockedUser Navigation Property
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
}
How do I store additional fields in the "link table" that is automagically created for me if I have two entities associated as having a many to many relationship?
I have tried going the "two 1 to many associations"-route, but I'm having a hard time with correctly configuring the cascading deletion.
Unless those extra columns are used by some functions or procedures at the database level, the extra columns in the link table will be useless since they are completely invisible at the Entity Framework level.
It sounds like you need to re-think your object model. If you absolutely need those columns, you can always add them later manually.
You will most likely need to expose the association in your domain model.
As an example, I needed to store an index (display order) against items in an many-to-many relationship (Project <> Images).
Here's the association class:
public class ProjectImage : Entity
{
public Guid ProjectId { get; set; }
public Guid ImageId { get; set; }
public virtual int DisplayIndex { get; set; }
public virtual Project Project { get; set; }
public virtual Image Image { get; set; }
}
Here's the mapping:
public class ProjectImageMap : EntityTypeConfiguration<ProjectImage>
{
public ProjectImageMap()
{
ToTable("ProjectImages");
HasKey(pi => pi.Id);
HasRequired(pi => pi.Project);
HasRequired(pi => pi.Image);
}
}
From Project Map:
HasMany(p => p.ProjectImages).WithRequired(pi => pi.Project);
Maps to the following property on project:
public virtual IList<ProjectImage> ProjectImages { get; set; }
Hope that helps
Ben
Suppose there is a many-to-many association between two types: User and Message, and the association class is defined as UserMessageLink with additional properties.
public class User {
public int Id {get;set;}
}
public class Message {
public int Id {get;set;}
}
//The many-to-many association class with additional properties
public class UserMessageLink {
[Key]
[Column("RecieverId", Order = 0)]
[ForeignKey("Reciever")]
public virtual int RecieverId { get; set; }
[Key]
[Column("MessageId", Order = 1)]
[ForeignKey("Message")]
public virtual int MessageId { get; set; }
public virtual User Reciever { get; set; }
public virtual Message Message { get; set; }
//This is an additional property
public bool IsRead { get; set; }
}