I have a class similar to the following:
public class MainClass
{
public int Id { get; set; }
public ChildType ChildType { get; set; }
public IChildData? Data { get; set; }
}
ChildType is an enum. There are implementations of IChildData for some, but not all, values.
The tables
MainTable
=========
Id int,
ChildType int,
[..Some more columns..]
SomeChild
=========
Id int,
ParentId int
[..Some more columns..]
ParentId points on the main table and StepType in the main table are used to tell which child table to load the subclass from.
I'm struggling with the mapping configuration.
I thought that I should use discriminators, but I can't figure how how to tell that the discriminator is for the property and not for MainClass.
This won't work:
var config = modelBuilder.Entity<MainClass>();
config.HasDiscriminator(x => x.ChildType).HasValue<SomeChild>(ChildType.SomeValue);
It complains that the SomeChild class does not inherit MainClass. How can I make EF understand that it's for the child's property?
No. Each MainTable row has zero or one child, where the MainTable.ChildType decides which child table to load from
Based on this comment you want to have a TPC/TPT inheritance for the child type with something like polymorphic foreign key in the main table, which is AFAIK is not natively supported by at least some SQL databases, which make it doubtful for support in the EF Core.
I would suggest to rethink your database design. Based on the provided info table-per-hierarchy looks like an appropriate solution:
public class MainClass
{
public int Id { get; set; }
public ChildDataBase? Data { get; set; }
}
public class ChildDataBase
{
public int Id { get; set; }
public ChildType ChildType { get; set; }
public MainClass Main { get; set; }
}
Note that 1) EF Core AFAIK does not work well with interfaces for entities so better to introduce a base class from the get-go 2) discriminator is required only for TPH and it is a property of the table containing the hierarchy other inheritance patterns do not need it and operate based on the entity to table mapping.
If you still want to use TPC or TPT then you will need either to rely on EF Core generating join queries (as mentioned before - discriminator is not used) or write the query logic yourself (potentially without setting up the relations and removing or marking corresponding properties as not mapped).
Read Foreign key constraints for TPC part of the doc it also can shed some light on the internal workings of the EF Core in this regard.
Related
I am experiencing problems in mapping a complex type on a model class (called Assignment) using EntityFramework 6.
I have the following Assignment model class (only the relevant members are shown):
public class Assignment
{
private AssignmentDueByInfo _dueIn;
public Assignment() {
_dueIn = new AssignmentDueByInfo(this)
}
public virtual AssignmentSettingInfo DueIn
{
get { return _dueIn; }
protected set { _dueIn = value; }
}
}
where AssignmentSettingInfo is defined as:
public class AssignmentSettingInfo
{
protected AssignmentSettingInfo(Assignment assignment)
{
Assignment = assignment;
}
protected readonly Assignment Assignment;
public virtual int? LessonId { get; protected set; }
public virtual Lesson Lesson { get; protected set; }
}
In Entity Framework 6, I have the following CodeFirst / Fluent API mapping for the Assignment class to a table in a database:
Property(t => t.DueIn.LessonId).HasColumnName("DueByLessonId");
HasOptional(x => x.DueIn.Lesson)
.WithMany(x => x.AssignmentsDue)
.HasForeignKey(x => x.DueIn.LessonId)
.WillCascadeOnDelete(true);
The mapping is throwing the following error:
The expression 'x => x.DueIn.Lesson' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty'
Why is this happening and how should it be fixed?
Check out the documentation (it's quite old, but still holds true):
https://msdn.microsoft.com/en-us/library/bb738472(v=vs.100).aspx
This states that complex types cannot contain navigation properties.
I guess this has to do with the fact they do not have primary keys and they are not managed separately by the context. If they have no PK and cannot be identified by the context, they cannot be 'relationship ends' either.
You are trying to make SQL object oriented. The classes you define that represent a database table should be POCOs: fairly simple classes with only get and set properties.
You are doing way too much with fields. If you have to fill a field in one of the table classes, think again. I've never seen an example where the class that represents a database table needed any field, other than a simple get/set property.
If one of the classes relates to another class, this is done by foreign key, not by this.
Class Assignment represents a database table. This table is supposed to have certain columns and relations to other tables using foreign keys. Class Assignment should not contain smart things that are not part of the table.
for example I see that Assignment has a field named _dueIn of type AssignmentDueByInfo. Outsiders can access this field, but for them this field is not an AssignmentDueByInfo, but an AssignmentSettingInfo.
Decide for yourself what columns the Assignment should have.
Is there a reason to put the columns for an AssignmentSettingInfo in a different table? If I look at your code there can't be an AssignmentSettingInfo without an 'Assignmentand there can't be anAssignmentwithout anAssignmentSettingInfo`. So why put them in separate tables?
Another problem is the relation between AssignmentSettingInfo and Lesson. Is this a zero-or-one to one? Follow these guide lines
If you'd put 'AssignmentandAssignmentSettingInfo` in one table your code would be like
class Assignment
{
public int Id {get; set;}
// every assignment has exactly one AssignmentSettingInfo
// use aggregation (in entity framework terms: complextype)
public AssignmentSettingInfo AssignmentSettingInfo {get; set;}
}
[ComplexType]
class AssignmentSettingInfo
{
// Every AssignmentSettingInfo has zero or one Lesson
public virtual Lesson Lesson { get; set; }
}
class Lesson
{
public int Id {get; set;}
// relation between Line and AssignmentSettingInfo
...
}
I couldn't figure out the relation between Line and AssignmentSettingInfo. Visit:
One-to-One
one-to-many?
If you really want the AssignmentSettingInfo in a different table, configure it as a one-to-one method as shown in the links
I'm trying to create a datastructure with entity framework to basically store property values of my objects. I want users to add properties to a class at runtime. The properties can be of different datatypes. (string/int/float etc..)
So I thought I needed some tables/classes as defined in the image below.
So my Object class contains a list of properties that are of a type defined in de propertydefinition class.
One hard thing is that values are stored in the table of the datatype of the propertie. (So a conditional foreignKey?)
Please give me some pointers on how to implement this by using Fluent API. Or other ideas on this subject. (I guess I won't be the first ;)
Werner
The EF entity model cannot be changed during Runtime (or at least is not designed for). You could use an infrastructure to store propertyname/propertyvalye with EF but I think is not the right choice (you lose most of the functionalities).
The best choice could be a NoSQL db, ADO.Net or, if only some objects can be personalized and other are fixed you could store the personalizable objects in XML/JSON in a text field.
I found this link
This helped me solve my "Table Per Type" question. I now have:
public abstract class PropertyBase
{
public int PropertyID { get; set; }
public string Name { get; set; }
}
public class TextProperty : PropertyBase
{
public string Value { get; set; }
}
public class IntProperty : PropertyBase
{
public int Value { get; set; }
}
In My Database Context I added:
modelBuilder.Entity<PropertyBase>()
.HasKey(p => p.PropertyID)
.ToTable("Properties");
modelBuilder.Entity<IntProperty>()
.ToTable("IntProperties");
modelBuilder.Entity<TextProperty>()
.ToTable("TextProperties");
The different types of properties (sub classes) are now stored in separate tables. The main abstract class contains all the other info. This worked fine for me.
Lets say we have a Person entity which fits table exactly:
public class Person
{
public int Id { get; set; }
public int Type { get; set; }
}
Now user have a type, and only if user is of specific type I want to be able to find out what is the Name of that type that person is. Right now I just add extra property on to the entity:
public class Person
{
public int Id { get; set; }
public int Type { get; set; }
public string TypeName { get; set; }
}
But I want to let my entities stay as clean as possible.
Do I need to create a separate "advanced entities" classes for this cases or how do I get around those cases?
PS. I am using stored procedures.
Your question is not clear but i think you are mixing view models with entities. If you want to store your user "Type" into the database then, yes you have to add a new field in your entity ( or use partial classes ) OR if you are using database first then add the field in your table and refresh your edmx . If you don't want to store it in database, just create a view model and use it on our UI.
Change that name to something specific about a person, whatever you want to track about a person (HairColor, EyeColor, Nationality, Size) anything but just plain "Type", which is a system base class in C#.
yes... use partial clases with extended properties inside them
I currently have the following Models in my EF Code First MVC project (edited for brevity):
public class Car
{
public int Id { get; set; }
public string Descrip { get; set; }
// Navigation Property.
public virtual CarColour CarColour { get; set; }
... + numerous other navigation properties.
}
public class CarColour
{
public int Id { get; set; }
public string ColourName { get; set; }
}
The CarColour table in the DB contains many rows.
In my project, I have about 10 of these sorts of tables, which are essentially lookup tables.
Rather than have 10 lookup tables (and 10 corresponding 'hard' types in code), I was tasked with implementing a more re-usable approach, instead of having loads of lookup tables, specific to Car (in this example), along the lines of having a couple of tables, one of which may hold the item types (colour, fuel-type etc.) and one which contains the various values for each of the types. The idea being that our model will be able to be re-used by many other projects - some of which will have potentially hundreds of different attributes, and as such, we won't want to create a new Class/Type in code and generate a new lookup table for each.
I am having difficulty in understanding the c# implementation of this sort of approach and hope someone may be able to give me an example of how this can be achieved in code, more specifically, how the above models would need to change, and what additional classes would be required to accomplish this?
your base entity must implement INotifyPropertyChanged and make it generic:
public virtual CarColour CarColour {
Get { return this.carColour; }
Set {
this.Carcolour; = value
OnPropertyChanged("CarColour");
}
}
For more info see :
patterns & practices: Prism in CodePlex.
http://compositewpf.codeplex.com/wikipage?title=Model%20View%20ViewModel%20(MVVM)
Greetings
Bassam
This is not necessarily specific to EF but I've been down this road and didn't really enjoy it.
I wanted to use a single table to represent 'generic' information and while I thought it was smart, it soon showed it's limitations. One of them being the complexity you need to introduce when writing queries to extract this data if you're performing more than just 'get colours for this car'.
I'd say, if your data is simple key/value and the value type is always going to be the same then go for it, it might even be worth having this a mere 'meta-data' for an object:
public class Car
{
public int Id { get; set; }
public string Descrip { get; set; }
public MetaData CarColours { get; set; }
}
public MetaData : Dictionary<int, string>
{
public MetaData(int group){}
}
Hypothetical table:
TableMetaData(int metaGroup, int metaId, string metaValue)
If you're hoping to store different types as your value and may need to perform joining on this data - avoid it and be a bit more specific.
I am defining a model in EF4 CTP5 where I need to map an inherited entity only when the value of an id is greater than 0. the code looks like this.
public class Parent
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Child : Parent
{
public int SchoolID { get; set; }
}
In the OnModelCreating method...
modelBuilder.Entity<Parent>().Map<Child>(
reg =>
{
reg.MapInheritedProperties();
reg.Requires("SchoolID").HasValue((int)<value greater than 0); <== Pseudo code
}).ToTable("Users");
Is this sort of thing possible? If not, is it possible to ignore the discriminator altogether?
Not sure about the 'is it possible' part, but it smells a little. I mean, it implies meaning in your data where there really should not be meaning; you'll wind up with long term maintainability problems. The discriminator is there not just to help the ORM figure out which record is of which type, it's also supposed to be (imo) a logical description of the type you're dealing with, for when humans run SQL queries against the db. It also can be used to help with index partitioning. I realize that TPH is a bit of denormalization anyway, but you still want to be able to design a well-indexed db with those in there.
I'd suggest just letting EF do the discriminator for you based on the type name, which iirc is the default.