I would like the below ICollection property in one of my data classes (let's call it "Foo")
public class Foo
{
[Key]
public int FooId { get; set; }
public string SomeValueOrOther { get; set; }
public virtual ICollection<string> AllowedBars { get; set; }
}
I can add the string values when using the entity context, but they don't "go anywhere". In other words, no table is generated to represent this relationship and therefore no values are saved. What I would expect is a table with two columns, one for "FooId" and one for "AllowedBar" and for EF to map this to the collection automatically (as it does in with complex types).
As this doesn't happen, I've had to create a class called "FooAllowedBar" with the two properties I've described above.
This displeases me because it's the only "join" type class I have in the entire project. It works, of course, so one box is ticked, but does anybody know of a way to get EF to generate a table for the string collection relationship? (Or int, or datetime etc etc)
It may well be, from the little info that's out there on EF (still!) that this type of functionality is not (yet) supported. By I'd just like to get close to a definitive answer.
Many thanks in advance,
Rob
EF can only work with entity classes. Each entity class must have defined primary key so the minimum in your scenario is:
public class StringData
{
public int Id { get; set; }
public string Data { get; set; }
}
or worse
public class StringData
{
[Key]
public string Data { get; set; }
}
Now you can define collection of StringData to map it as related table:
public virtual ICollection<StringData> AllowedBars { get; set; }
I know this is not best practice in all cases, but I think there are cases where storing a comma seperated list of your array in a column is a good way to solve this problem.
Conditions include:
The list is not going to be long
You don't need to search for entities based on the values in that list
It could also be a good idea if one entity has multiple string lists in it that would create lots of joins.
In those cases I would solve it by having two properties for the list. One being the comma seperated list used by EF and the other a list that you can use when accessing the items in the list like this:
[NotMapped]
public List<String> AllowedBars { get; set; }
/// <summary>
/// Comma seperated list of AllowedBars
/// </summary>
public String AllowedBarsList
{
get { return String.Join(",", AllowedBars); }
set
{
if (String.IsNullOrWhiteSpace(value))
{
AllowedBars.Clear();
}
else
{
AllowedBars = value.Split(',').ToList();
}
}
}
You need to initialise AllowedBars to be an empty list in the constructor.
You don't need to use the [NotMapped] attribute as this collection won't be used anyway, but I think it makes the intent clearer.
This won't work. The reason for this is, that with relational databases, you can't really save arrays or a collection of things in fields. And since every property in your class will be mapped to a database-field, the only way to collections is via a one to many relationship. So you need the join. So it's not really a limitation of EF, but of relational databases.
There are people that solve that by saving XML or CSV to string fields in the table. But this is considered very bad style, so don't do it. I recommend you just have to accept the join. It's not hurting anyone anyway.
You have not define your class tables appropriately. Suppose you have two Tables Foo and FooBar. And there is a one-to-many relationship b/w Foo and FooBar. Then you will define the classes as below.
Foo
public class Foo
{
public int FooId { get; set; }
public string SomeValue { get; set; }
public virtual ICollection<FooBar> FooBars { get; set; }
}
FooBar
public class FooBar
{
public int FooBarId { get; set; }
public string SomeProperty { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
}
This will create two tables with Foo having two columns and FooBar having 3 columns including the FooId depicting one-to-many between Foo and FooBars
Related
I am using the table per type strategy with a class named Assets as my super-type and another class PropertyAsset inheriting it. However, there are two classes inheriting the PropertyAsset class, named InternalProperty & ExternalProperty.
Asset Table
public class Asset : Entity, IMustHaveCompany
{
public int CompanyId { get; set; }
public string? SerialNumber { get; set; }
public decimal ExpectedLife { get; set; }
public decimal EstimatedValue { get; set; }
public DateTime PurchaseDate { get; set; }
#region Navigation Properties
public virtual Company.Company? Company { get; set; }
#endregion
}
Property Asset Table
public class PropertyAsset : Asset
{
public int Rooms { get; set; }
public int Bathrooms { get; set; }
public decimal LeaseRate { get; set; }
public decimal SqFeet { get; set; }
}
External Property Table
public class ExternalProperty : PropertyAsset
{
public decimal LeaseRatePerSquareFt { get; set; }
public decimal SquareFoot { get; set; }
public decimal AnnualIncome { get; set; }
public decimal MonthlyLease { get; set; }
}
Internal Property Table
public class InternalProperty : PropertyAsset
{
public decimal PurchasePrice { get; set; }
public PaymentMethod PaymentMethod { get; set; }
public decimal DownPayment { get; set; }
public decimal PaymentsRemaining { get; set; }
public decimal Borrowing { get; set; }
public decimal AnnualTax { get; set; }
}
How would I create let's say the InternalProperty entity using this approach, with the values reflecting upwards? My thoughts are that I would need to use a DTO and map it with AutoMapper, but when thinking about it, I doubt that approach will work, or if it would, I'd need to create three entities separately (InternalProperty, then PropertyAsset and somehow use the Id of the newly created internal property, and then finally the Asset table itself.)
Am I using the TPT approach incorrectly in this instance?
I'd need to create three entities separately (InternalProperty, then PropertyAsset and somehow use the Id of the newly created internal property, and then finally the Asset table itself.)
Negative. Inheritance represents is a relationship, so you just need to create a single object of the desired type, add it to the context and EF Core will do the rest.
this, along with polymorphic queries is the whole purpose of EF Core database inheritance strategies. The only difference between different strategies (TPH, TPT) is how the data is stored (single table with discriminator column and union of all base and directly or indirectly derived entities data vs multiple tables storing just the data associated with the corresponding level of the entity hierarchy), respectively how is queried. But in both cases, you simply add assignable object instance to the corresponding set. For instance, if you have
var instance = new InternalProperty { ... };
then you can use any of the following
context.Set<Asset>().Add(instance);
context.Set<PropertyAsset>().Add(instance);
context.Set<InternalProperty>().Add(instance);
context.Add(instance);
(the last accepts object type argument).
All that works because EF Core uses the actual type (basically instance.GetType() except for proxy types) for handling the operation.
Similar happens when querying. Polymorphic queries means that anytime you query a base level, it includes the level plus all direct and indirect derived levels, with correct actual instance types.
For instance, if you query context.Set<Asset>, you would get all Asset, PropertyAsset, ExternalProperty and InternalProperty instances (with correct actual type). context.Set<PropertyAsset> will include all PropertyAsset, ExternalProperty and InternalProperty entities while context.Set<ExternalProperty> and context.Set<ExternalProperty> will include only the corresponding items.
Shortly, it is combination of query filter and query materialization creating correct object type and populating it with data. All that done automatically by EF Core.
So the usage of TPT (or TPH) is pretty valid for such object model. Just mark the classes (if any) which are not supposed to be creatable as abstract. It won't change the database structure (especially for TPT), but will allow generating more efficient queries (filters and joins) by EF Core.
I am getting this error
System.InvalidOperationException: The entity type 'MyType' requires a primary key to be defined
Trying to use DbContext, DbSet etc.
I don't think MyType does need a primary key though?
Why am I being told to give it a primary key...
I have created some models that look like
public ContainerType
{
public string Id { get; set; }
public int SomeData { get; set; }
public MyType Foo { get; set; }
public MyType Bar { get; set; }
}
public MyType
{
public string SomeProperty { get; set; }
public List<OtherType> OtherTypes { get; set; }
}
and I try to create a DbSet<ContainerType> Items, but whenever I do Items.Count() I am getting that InvalidOperationException.
Why?
By definition, an "entity" is an object that has an identifier, in contrast to a "value object", where the identity of is the sum total of all its data. For example, you could have a class like:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
However, multiple people could have the same name, so the mere values of FirstName and LastName is not enough to uniquely identify a person. You need an explicit identifier:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now, you can distinguish between two different people with the same name.
There's no coincidence that EF is Entity Framework. It's literally a framework for working with entities, i.e. objects with identifiers. That's your PK, and it is required for all entities, or else they would not in fact be entities.
If you truly feel you don't need PKs (though I would consider that assumption suspect), then EF is not the right solution for you. You might instead want to use IDistributedCache, Session, or a NoSQL store. Either of those will allow you work with any form of data you like (though you will need to serialize to/from JSON). However, you'll likely find that you'll still need ids of some sort or another.
I don't think MyType does need a primary key though?
You need a Key attributed (assuming you're using DataAnnotations) property on each class that part of your EF models. This is required to set up the foreign key relationships in the underlying relational database.
whenever I do Items.Count() I am getting that InvalidOperationException
Do you only get this error on .Count() or do all of your operations on this DbSet throw the same exception due to an invalid model?
I have two table that have exactly the same schema. How can I create 1 entity in my code and map that single entity to the two different tables?
One option I tried was to create a second entity that inherits from the first, but have no additional properties defined:
public class EntityA {
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public string PropertyD { get; set; }
public string PropertyE { get; set; }
}
public class EntityB : EntityA {
}
In the database, I would have tables EntityA and EntityB with both having the exact same schema.
Using EF 6 or earlier, it seems that I had to do something like this:
modelBuilder.Entity<Entity>()
.Map(map => { map.ToTable("EntityA") })
.Map(map => { map.ToTable("EntityB") });
It however seems that the Map method cannot be found when I try to do this in EF Core.
What is the best solution for this, or would I just have two entities in C# where they look exactly the same?
Have both entities derive from a common base class which has all the common properties. That way, they share the same class to be shared amongst your code after a cast, but still keep the originality of being different as needed in the future.
⚠️ The downside is that you will, by hand, update these classes during any future changes.
I'm preparing a project's data structure (code-first) in an ASP .NET Core 2 application, with the help of Entity Framework. This specific relationship I have no experience with: the user has to be able to choose diseases with checkboxes, and we have similar choices: cancer type, dietary, etc..
I have more than two tables like the ones on the picture, which will be referred from the UserKitProperties table. This table should work like a connector table, connects the user entity with other entities.
userid1 | cancertypeid1
userid2 | dietaryid1
userid1 | cancertypeid2
userid3 | dietaryid1
How should this be specified in the code, to support this relationship? I was thinking on doing a base class and maybe refer to that id. And this is the connector class..
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
// this should be used for cancer type, dietary, etc..
public long PropertyID { get; set; }
/* Instead of using two classes' ids, maybe call the base class' id
[ForeignKey("PropertyID")]
public CancerType CancerTypes { get; set; }
[ForeignKey("PropertyID")]
public Dietary Dietaries { get; set; } */
}
Thank you in advance for your suggestions! :)
The following should work:
public class Property
{
public long PropertyId { get; set; }
}
public class CancerType : Property
{
// Your code
}
public class Dietary : Property
{
// Your code
}
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long PropertyID { get; set; }
[ForeignKey("PropertyID")]
public Property Property { get; set; }
}
But as this MS doc mentions, setting up such inheritence will use a special Discriminator
column in the base class table, to represent what specific type is stored in a row.
I personally would resort to having nullable fields instead in order to not add more complexity. This doesn't enforce, however, that PatientProperties only has one property, which is a considerable minus:
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long? CancerTypeID { get; set; }
[ForeignKey("CancerTypeID")]
public CancerType CancerType { get; set; }
public long? DietaryID { get; set; }
[ForeignKey("DietaryID")]
public Dietary Dietary { get; set; }
}
Instead of thinking about the database layout first, you should think about how you would represent this relationship in code. After all, you are doing a code-first approach.
There are basically two choices you could choose: Either the patient has multiple properties, one for each property type, or there is just a single collection for all properties:
public class Patient
{
// …
// option 1
public CancerType CancerType { get; set; }
public Dietary Dietary { get; set; }
public OtherProperty OtherProperty { get; set; }
// option 2
public IList<PatientProperty> Properties { get; set; }
}
Both of these options have their advantages and disadvantages. While option 1 is very explicit and enforces a single value for every type, it also requires you to have a (class) property for every (patient) property. So if you extend your model later, you will have to adjust your patient model.
Option 2 has the benefit that it can just collect everything. So you can just add properties to your patient without having to modify the model later if you introduce new properties. In addition, it would also directly support multiple selections for a single kind. On the downside, it does not verify anything on its own, so you need business logic to actually enforce your rules.
Moving onto the database, for option 2 you obviously need a link table since that is a many-to-many relationship now. Since you only have a link to the base type PatientProperty but you actually want to talk about the concrete type, you will need some kind of discriminator. Discriminators are basically just a notation to additionally store the kind of object in the database.
When storing data with inheritance, what is commonly done is “table-per-hierarchy”. That means that all types within the hierarchy of the PatientProperty base type will share the same table. A discriminator column is used to specify the type, and additional properties that some property types may have are implemented with nullable columns. This setup works out of the box with Entity Framework and is described in this chapter in the documentation.
The other approach, “table-per-type” is not supported in EF Core, so if you wanted to follow that, you would have to implement it yourself. But in your case, where the property types are mostly very similar, I would actually argue against that and actually keep them in the same table.
For option 1, as long as you only have a single property of each kind assigned to the patient, things are a bit simpler. Since you don’t have many-to-many there, you don’t actually need a link table. You just need to store the id for each linked property type in the patient model, as shown in the above UML. Doing that, you can also keep the property types as separate types that do not share a single table in the database.
I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
In my model I have an entity (let's call it A) which is connected to other four entities through one-to-many relationships (that are represented as lists in the model). In order to populate the tables recursively when inserting an object of A in the database I have defined the relations to use Cascade on both read, insert and delete.
In order to test if I did everything correctly I created an object of type A and populated the including lists, and finally I have inserted it into the database. The strange thing is that, for 2 of the 4 including lists the insertion went well, and all the connected objects are inserted. For other 2, instead, only the first object of the list is inserted in the database. To be clear, I am checking the database content directly with a db browser.
The following is an example of one of the objects for which only the first element of the list is inserted.
public class Username : Entity
{
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
public Username(string username)
{
Name = username;
}
}
This is instead one of the objects for which the insertion is correct.
public class AnAddress: Entity
{
public string Address
{
get;
set;
}
public AddressType Type
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
}
To be clear, the base object Entity contains the definition of the primary key:
public abstract class Entity
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public Entity()
{
Id = -1;
}
}
And this is the way the relationships are defined:
public class A : Entity
{
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<AnAddress> Addresses
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Username> Usernames
{
get;
set;
}
}
I then create an A object by initialising it with two lists (List and List) in the same identical way.
I finally insert the object in the database with
c.InsertWithChildren(entity, recursive: true));
Where entity is of type A and c is the connection object.
Do you have any clue about the motivation of this strange behaviour?