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.
Related
I have "business entities" and their counterpart for saving them to Azure Storage Table, which requires a few additional properties.
// MyData is the business entity with a few properties
public record MyData_AzureTable : MyData, ITableEntity
{
// Required properties for storing data to Azure Storage Table
public string PartitionKey { get; set; } = "";
public string RowKey { get; set; } = "";
public DateTimeOffset? Timestamp { get; set; }
public ETag ETag { get; set; } = new ETag();
}
I am getting tired of having to duplicate each business entity with its AzureTable counterpart but I can't find the correct pattern to use. Something like that, except it's illegal to inherit from a type parameter.
public record AzureTable<T> : T, ITableEntity
{
public string PartitionKey { get; set; } = "";
public string RowKey { get; set; } = "";
public DateTimeOffset? Timestamp { get; set; }
public ETag ETag { get; set; } = new ETag();
}
What pattern should be used for adding properties to a base class?
The object saved to Azure Table Storage needs to be "flat" (tabular data as property values, no hierarchical data or encapsulation)
Not necessarily a pattern but abstract classes may fit well for your need here. Check out the docs: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract
In conclusion, those classes have a base one (which would be your always default AZ properties) and all the other classes than inherit from the abstract one will contain those properties as well without needing implementation (like an interface would) but you can extend the children and add more custom properties to each one.
There is no such pattern, currently C# does not support something like traits (though in some cases similar can be achieved with default interface member implementations) and/or multiple inheritance (basically MyData_AzureTable should inherit from both MyData and AzureTable). If you are really tired of writing "duplicates" for data - you should consider using source generators - you can write quite a simple one which will generate azure tables classes for all required classes (for example marked with special attribute like GenerateAzureTable). Potentially it can generate also some useful methods for mapping, copy constructors and so on.
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.
I have an parent class and two child like these:
public class Parent {
public Guid Id { get; set; }
public string Name { get; set; }
}
public class FirstChild {
public string IdentityCode { get; set; }
}
public class OtherChild {
public string RegistrationCode { get; set; }
}
There is a question: Is it a good approach to store these two inherited classes in the same Index inside ElasticSearch?
I see there is a _type property that is added to my docs after they are stored in DB but it has always "doc" value.
I test this code to fill it but it seems it is not working this way.
await ElasticClient.IndexAsync<FirstChild>(child, m => m.Index(IndexName));
And Also, I found this question on SO for retrieving my entries from DB but it is outdated and the API is changed and no more accessible.
I want to know if it is a good approach to store sibling data in the same index how can I do this properly.
As of ES 6.0, it is not possible anymore to store multiple types inside the same index, i.e. the _type field you're referring to will always be either doc or _doc. In ES 8.0, the _type field will be removed altogether.
However, if it makes sense for your use case, you can still decide to store several types inside a single index using a custom type field that is present in your document.
You should strive to only store in the same index data that share the same (or very similar) mapping, which doesn't seem to be the case for Parent, FirstChild and SecondChild, but if you add a public string type property to your classes you can still do it.
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.