I've been trying to figure out how to do the following (although my research did not help): I have the these three classes:
public abstract class Classifier
{
public int ClassifierId { get; set; }
public string ClassifierName { get; set; }
public DateTime DateAdded { get; set; }
}
public class ManualClassifier : Classifier
{
public int ManualClassifierId { get; set; }
public string user_name { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string password { get; set; }
}
public class ToolClassifier : Classifier
{
public int ToolId { get; set; }
public string ToolName { get; set; }
}
Both the ManualClassifier and ToolClassifer inherit from Classifier. I'm using EF Core to map this to a database but the question is the following: I've already searched a bit and I must make use of a descriminator which basically is an implicitly created column that will say the type of, in this case, classifier. So far so good. The issue arises when I have a property called ManualClassifierId as well as a ToolId. I want this two properties to map to the ClassifierId property. So in the table representing the entity Classifier, the ClassifierId property will either be the ManualClassifierId or the ToolId.
How can I achieve this mapping? Also, this solution would mean that both child classes would both have empty fileds in the tables (due to inheriting the three properties from the Classifier class). Is there a better solution? Perhaps just erase the Id's from both child classes a let them inherit the parent one?
Thank you in advance!
To use the same column name in both classes, you can add a Column attribute to both properties. Then they will both use that column name in the database. See ColumnAttribute(String).
Use it like this:
public class ManualClassifier : Classifier
{
[Column(Name="ClassifierId")]
public int ManualClassifierId { get; set; }
...........
}
Do the same with ToolId.
Related
My use-case:
I'd like to store a representation of a file tree in my local (SQLite) database using EF.
My model will be a simplified copy a much larger model on a remote SQL database (also in EF)
I'd like to use one, generic entity that self-refers to create a tree structure, and derives its 'type' field from one of the original entity types (FiletypeA, FiletypeB, Folder etc.. using the interface IFileSynchronisable)
I figured the best way was to make the class generic, and deriving a string field from the type using nameof(T) and Type.GetType("FiletypeA"), but I've got stuck trying to instantiate the class when building the model:
public class FileSyncObject<T> where T : class, IFileSynchronisable
{
[Key]
public Guid Id { get; set; }
public long ObjectId { get; set; }
//Can I derive T from some 'ObjectType' field in the record?
public string ObjectType { get { return nameof(T); } }
public long ProjectId { get; set; }
public string AmazonS3Path { get; set; }
public bool IsActive { get; set; }
public string Version { get; set; }
public Guid LocalParentId { get; set; }
public FileSyncObject<T> LocalParent { get; set; }
public virtual ICollection<FileSyncObject<T>> LocalChildren { get; set; }
}
What's the best approach? Is this even possible?
This is the class:
namespace backend
{
[Table("Products")]
public class Product
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Category { get; set; }
public string? Short { get; set; }
public string? Description { get; set; }
[Required]
public float Price { get; set; }
public string? MainImage { get; set; }
public float Disccount { get; set; }
public string[]? Images { get; set; } // List<string>
}
}
I've tried to run EF migrations but it appears that [] is not supported and, if I make it a List, it will ask for this List's key but it's just an array of strings
What am I doing wrong? Is not possible to add arrays as classes properties in EF?
It depends on the use case. Does the data be used in a relational child table way (1:many)? Or is it really just a list of some urls that don't have any further relations within the database?
For the first case, take a look at Caius answer. The second approach would be to write a type converter and register it within EF core. In that case your underlying type in the database would be some kind of string (e.g. nvarchar(max)) and EF core makes the conversion on the client side
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Product>()
.Property(e => e.Images)
.HasConversion(
v => JsonSerializer.Serialize(v),
v => JsonSerializer.Deserialize<string[]>(v));
}
Instead of JSON you could also use some other approach like string.Join() and string.Split(), that's up to you. Further information can be found at Microsoft documentation.
What's wrong with doing what a relational database would expect:
namespace backend
{
[Table("Products")]
public class Product
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Category { get; set; }
public string? Short { get; set; }
public string? Description { get; set; }
[Required]
public float Price { get; set; }
public string? MainImage { get; set; }
public float Disccount { get; set; }
public ICollection<ProductImage>? Images { get; set; } //make it a new hashset in the constructor, btw
}
[Table("ProductImages")]
public class ProductImage
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Url { get; set; }
public long ProductId { get; set; }
public Product? Product { get; set; }
}
this way you can add more data to the image, like "front view", "side view", "version 2" etc, and EF can map it like it knows how to; as a separate table that is 1:M related
Is not possible to add arrays as classes properties in EF?
Technically, the ICollection<ProductImage> is a property that is an "array".. but out-of-the-box, not arrays of primitives, no
string is primitive type. you can't save list of primitive types in entity framework instead you can save your data in one property which is a single string then define another property to just get and set the main string property.
public class example {
public string AttachmentsString { get ; set; }
[NotMapped]
public List<string> Attachments {
get => !string.IsNullOrEmpty(AttachmentsString) ? AttachmentsString.Split(",").ToList() : new List<string>();
set => AttachmentsString = string.Join(",", value);
}
}
then in your controllers or service just manipulate with Attachments property.
I have a sqlite database which has some tables and columns like the following:
int Id
text Name
text Comment
...
And my object in my project looks like this:
Public Class Entry {
public int Id { get; set; }
public String Name { get; set; }
public String Comment { get; set; }
public String Additional { get; set; }
}
This can happen, because my programm need to handle different versions of the database.
EF Core now trys to access the Additional field of the database but returns an error that it cannot find the field. (Expected behaviour)
Now my question is, if there is a way to ignore this error and return a default value for the property?
I could bypass the error by making the properties nullable. But i don't want to check each property with .HasValue() before accessing it. Because the real database has 50+ columns in the table.
https://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-first.aspx
Put NotMapped as an attribute on the Additional field:
using System.ComponentModel.DataAnnotations.Schema;
Public Class Entry {
public int Id { get; set; }
public String Name { get; set; }
public String Comment { get; set; }
[NotMapped]
public String Additional { get; set; }
}
This tells EF that the field is not a column in the database.
I would advise you to split your domain object from that persisted dto object. That way you can have different dtos with different mappings. Now you can instantiate your domain object with your dto and decide inside your domain object what values are the correct default values.
public class Entry
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public string Additional { get; set; }
}
public class EntryDtoV1
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
public class EntryDtoV2
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public string Additional { get; set; }
}
Now you only need to create some kind of factory that creates the correct repository depending on what database version you query.
I'm working on application in which the user defines classes using dedicated editor. The result of this step is db table which holds a class name and list of properties attached to it by the user.
The classes can only hold only primitive types, but struct, as parameters.
The next step is to load the table rows as dynamic objects to another application.
Beside using reflection, is there another way to convert table row to POCO?
These are the models:
[Description("represents the base model")]
public class
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
}
public class ParameterModel : BaseModel
{
public string Type { get; set; }
public string DefaultValue { get; set; }
}
[Description("Represents dynamic activity")]
public class ActivityModel : BaseModel
{
public List<ParameterModel> Parameters { get; set; }
}
Thank you very much.
Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.