Find a collection property using reflection then add to collection - c#

I am trying to create a generic function to add relational records in entity framework. I am working on the following solution. Though if I am going about this all wrong could you direct me in the right direction.
I have the following to classes. Also to note this is a code first approach in entity framework.
public class MainTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MainTableID { get; set; }
public int LookupID { get; set; }
[ForeignKey("LookupID")]
public virtual LookupTable Lookups { get; set; }
}
public class LookupTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int LookupID{ get; set; }
public string LookupText { get; set; }
public virtual ICollection<MainTable> MainTable { get; set; }
}
I then have the following code that I am trying to add an relationship between MainTable and LookupTable. I am first passing the two table class types and passing the record to add and the name of the collection property.
The problem is the "collectionField" is NULL whenever I try to cast it (ICollection(MainTable)). I need to "collectionField" to become of type ICollection(MainTable) so I can add the MainTable record, unless I am going about this the wrong way.
public void AddRelationalRecord<MainTableType, LookupTableType>(MainTableType recordToAdd, string collectionFieldName)
where MainTableType: class
where LookupTableType: class
{
var collectionField = typeof(LookupTableType)
.GetProperties()
.Where(prop => prop.Name == collectionFieldName)
.FirstOrDefault();
collectionField.Add(recordToAdd);
}

From the comments I've decided to change my approach to this. If anyone is interested the below code works.
public void AddRelationalRecord(object recordToAdd, object recordWithCollection, string collectionFieldName)
{
var collectionProp = recordWithCollection.GetType().GetProperty(fieldName);
collectionProp.PropertyType.GetMethod("Add").Invoke(aaa.GetValue(recordWithCollection, null), new object[] { recordToAdd });
}

Related

Entity Framework - Load related entity on related entites (one to many to one)

I'm trying to load an object (a) and that object has a collection of objects (b), each object:b has single objects attached to them.
I've cut down the code below to show you how it's setup.
I can without any problems load 'MyImprovement'.
I can in that same moment load the collection of 'MyCondition'.
But what I fail to load is 'MyComponent' that is referenced in the 'MyCondition'...
I managed to find some samples using .Include and .ThenInclude, however, .ThenInclude does not exist from what I can see?
I've tried every possible aspect of this and I still think that it should be possible to do,... right?
Anyone who's up for the task? I'm completely lost right now, so please help!
Thanks in advance!
/Karl
public class MyImprovement
{
[Key]
public int MyImprovementId { get; set; }
public virtual ICollection<MyCondition> Conditions { get; set; }
}
public class MyCondition
{
[Key]
public int MyConditionId { get; set; }
public int? ComponentId { get; set; }
public virtual MyComponent ConditionalMyComponent { get; set; }
}
public class MyComponent
{
[Key]
public int ComponentId { get; set; }
public string PDNumber { get; set; }
}
// Code to load the first 2 levels of objects
MyImprovement improvement = dbx.MyImprovements
.Include("Conditions")
.Where(x => x.ImprovementId == id)
.First();
If you want to eager-load complex entity graph, you can combine multiple .Include()- Methods with following style:
MyImprovement improvement = dbx.MyImprovements
.Include("Conditions")
.Include("Conditions.ConditionalMyComponent")
.Where(x => x.ImprovementId == id)
.First();

EF Core - How to create inherited entity types that contain foreign key columns

I am using the table per hierarchy approach for achieving inheritance in entity types. I have 3 classes defined:
Room - Base class
SubMapRoom - Inherits from Room
OverviewRoom - Inherits from Room
In the DB, I just have 1 table called Room that has both the SubMapRoom and OverviewRoom columns in it. It also contains the Discriminator column for specifying which type it is.
First, I attempted to move all of the SubMapRoom columns in the Room class into the SubMapRoom class. 1 of the columns contains a foreign key to a different table called Status. After doing this, I tried specifying the foreign key relationship for the SubMapRoom entity type in OnModelCreating(). However, I get a compile error when I try to do this. In the EF Core OnModelCreating() method, I have this code (marked the line that contains the error below):
modelBuilder.Entity<SubMapRoom>(entity =>
{
entity.HasOne(d => d.UnassignedDoctorStatus)
.WithMany(p => p.Room) **ERROR HAPPENS HERE**
.HasForeignKey(d => d.UnassignedDoctorStatusId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_Room_UnassignedStatusID");
});
modelBuilder.Entity<Room>()
.HasDiscriminator<int>("RoomType")
.HasValue<SubMapRoom>(1)
.HasValue<OverviewRoom>(2);
I get this error:
Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
I know that I can solve this by changing the other class (Status) to use the inherited type instead of the base type for the navigation property, but that seems like the wrong way to go. I feel like I am missing something here. What would be the correct way to define a foreign key relationship in an inherited entity type?
[EDIT]
Here are the classes for the 4 models I have referenced here:
public abstract class Room
{
public Room()
{
InverseLinkedRoom = new HashSet<Room>();
}
public int Id { get; set; }
public int SubMapId { get; set; }
public string MapLabel { get; set; }
public string RoomLabel { get; set; }
public int LeftCoordinate { get; set; }
public int TopCoordinate { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int? LinkedRoomId { get; set; }
public int RoomType { get; set; }
public Room LinkedRoom { get; set; }
public SubMap SubMap { get; set; }
public PatientQueue PatientQueue { get; set; }
public ICollection<Room> InverseLinkedRoom { get; set; }
}
public class SubMapRoom : Room
{
public int? UnassignedDoctorStatusId { get; set; }
public Status UnassignedDoctorStatus { get; set; }
}
// Note: Have not yet attempted to move base class members in here
public class OverviewRoom : Room
{
}
public partial class Status
{
public Status()
{
Room = new HashSet<Room>();
}
public int Id { get; set; }
public string EnumId { get; set; }
public bool Active { get; set; }
public bool IsFastBlink { get; set; }
public ICollection<Room> Room { get; set; }
}
Thanks for the help everyone. I reviewed my DB schema and decided to make some changes that make this problem go away. It actually turns out that my new schema is easier to use in the code than I originally thought. In fact, it's a lot easier. I think I was trying to overengineer this. So sometimes, the solution is to review your schema and figure out if it even makes sense in the first place. Basically, what I did was move the inherited classes to a separate table with a separate ID. Because, at the end of the day, they are logically separate types of entities and only related in terms of the data. In the code, they serve much different purposes even though they share some of the same columns.
At the end of it all, the only disadvantage of this approach is that I am violating DRY on another table (there are 5 repeated columns in it). Otherwise, a lot of other operations are easier to code than before. I am willing to live with that instead of dealing with all of this for now. Later, I can try to use Table Per Hierarchy if I am having to add tons of new columns to both tables.

Entity Framework Update nested list

I use Entity Framework 6 (Code First). I have a class:
public class DialogSession {...}
And another class with a list of DialogSession objects:
public class DialogUser
{
public int Id { get; set; }
public List<DialogSession> DialogSessions { get; set; }
}
I add DialogSession object to the list and then execute context.SaveChanges() as follows:
dialogUser.DialogSessions.Add(dialogSession);
context.SaveChanges();
But the foreign key of the dialogSession record still Null:
I've tried using many methods on the web like the follows but withoyt success:
context.DialogUsers.Attach(dialogUser);
context.Entry(dialogUser).State = EntityState.Modified;
context.SaveChangesExtention();
Does anyone know how to save inner objects (like the list) in the database using Entity Framework (6)?
From your question is not clear which relationship type do you have, so I guess you have One-to-Many and something like this should works:
public class DialogSession
{
public int DialogSessionId { get; set; }
public virtual DialogUser DialogUser { get; set; }
}
public class DialogUser
{
public int DialogUserId { get; set; }
public virtual ICollection<DialogSession> DialogSessions { get; set; }
}
Take a look at example how properly configure this type of relationship in this article.
If I am not wrong you should add
dialogUser.DialogSessions.Add(dialogSession);
context.Entry(dialogUser).State = EntityState.Modified;
context.SaveChanges();
This will mark the entity as modified and then the changes should be reflected on the db.
This could be done a more efficiently by marking singular properties as modified
dialogUser.DialogSessions.Add(dialogSession);
context.Entry(dialogUser).Property(u => u.dialogSession).IsModified = true;
context.SaveChanges();
Give it a try :)
Please see: https://msdn.microsoft.com/en-us/library/jj591583(v=vs.113).aspx
You should use a virtual list for the child entity, and ensure that the DialogSessions class also refers back to its parent with a DialogUserId property (so named by convention)
The below works for me.
using System.ComponentModel.DataAnnotations;
public class DialogSession
{
[Key]
public int DialogSessionId { get; set; }
public int DialogUser { get; set; }
}
public class DialogUser
{
[Key]
public int DialogUserId { get; set; }
public virtual ICollection<DialogSession> DialogSessions { get; set; }
}

EF Code First - How do you specify foreign key name for child object table used by different type of parents

I've got some objects that look like this:
abstract public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public int Ordinal { get; set; }
}
[Table("DropDownField")]
public class DropDownField : Field
{
public virtual List<FieldOption> Options { get; set; }
}
[Table("RadioButtonField")]
public class RadioButtonField : Field
{
public virtual List<FieldOption> Options { get; set; }
}
public class FieldOption
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
In my database, it ends up creating the a FieldOptions table using Code First. However, it creates the following columns:
Id
Name
Value
DropDownField_Id
RadioButtonField_Id
What I'd like to see is just one Field_Id in this table since the Id of a field has to be unique across the different types of fields.
Is there a way to do this? I've done some searching but I must not know the right search terms to use to find the answer.
imho, what you want, from a relational database point of view, is a column (Option.FieldId) being a foreign key to 2 tables DropDownField and RadioButtonField.
That is whenever you insert an option, FieldId must reference an existing DropDownField AND an existing RadioButtonField.
That is at least weird.
I don't think this can/should be achieved.

How to make proper code-first relations

I'm fairly new to Entity Framework and feel more in control using the Code-First pattern rather than DB-First.
I was wondering what is more preferred when it comes to programmatically setting up ForeignKey relations between the entities.
Is it better to declare a FK_ property in the class which relates to the another class or is it better to declare an IEnumerable<> property in the class that gets related to?
public class IRelateToAnotherClass
{
...
public int FK_IGetRelatedToByAnotherClass_ID { get; set; }
}
or
public class IGetRelatedToByAnotherClass
{
...
public IEnumerable<IRelateToAnotherClass> RelatedTo { get; set; }
}
It all depends on what type of relationships you want between your entities (one-to-one, one-to-many, many-to-many); but, yes, you should declare foreign key properties. Check out this site for some examples.
Here's a one-to-many for your two classes:
public class IRelateToAnotherClass
{
public int Id { get; set; } // primary key
public virtual ICollection<IGetRelatedToByAnotherClass> IGetRelatedToByAnotherClasses { get; set; }
}
public class IGetRelatedToByAnotherClass
{
public int Id { get; set; } // primary key
public int IRelateToAnotherClassId { get; set; } // foreign key
public virtual IRelateToAnotherClass IRelateToAnotherClass { get; set; }
}
and with some Fluent API mapping:
modelBuilder.Entity<IGetRelatedToByAnotherClass>.HasRequired<IRelateToAnotherClass>(p => p.IRelateToAnotherClass).WithMany(p => p.IGetRelatedToByAnotherClasses).HasForeignKey(p => p.Id);
If I understand what you're asking correctly, you'd want both. You want an int FK property and an object property to use as the navigation property.
The end result would look something like this:
public class Employee
{
[Key]
public int EmployeeID { get; set; }
[ForeignKey("Store")]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual Store Store { get; set; }
}
public class Store
{
[Key]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual List<Employee> Employees { get; set; }
}
If you haven't already, take a look at navigation properties and lazy-loading. Note that EF is clever enough to figure out that an int StoreID property corresponds to an object Store property, but if they are named differently (such as without the ID suffix), you must use the [ForeignKey] annotation.

Categories