In EF.Core Map Single Entity to two tables - c#

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.

Related

Entity Framework : invalid object name "TableName1"

I have a problem with Entity Framework, I think I have a problem defining the one to many mapping, but I can't seem to find it.
When I try to save my changes and add my info to the database I get the following error:
Invalid object name 'Results1'
With Results being the name of one of my tables, for some reasons it's adding the 1 at the end.
I have a Results table where I need to store an Id and several other information regarding results of a test, then I have a Reasons table where I need to store several reasons for each Result.Id.
This is how I defined the Reasons class:
public class Reasons
{
public int Id { get; set; }
public int ResultId { get; set; }
public string Description { get; set; }
public Results Results { get; set; }
}
Then I have the Results class:
public class Results
{
public int Id { get; set; }
//Other properties
public ICollection<Reasons> Reasons { get; set; }
}
This is my Reasons configuration:
configurator.
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
configurator
.HasRequired<Results>(s => s.Results)
.WithMany(g => g.Reasons)
.HasForeignKey<int>(s => s.IdResults);
Solved: the only problem was that the "Reasons" and the "Results" class did not completely match the data model. Once that was repaired the problem disappeared.
Rename your foreign key in Reasons class from ResultId to ResultsId or explicitly use [ForeignKey("Results")] attribute above it. From here:
EF makes a property as foreign key property when its name matches with
the primary key property of a related entity
In your case it should be:
// Foreign key for Results
public int ResultsId { get; set; }
// Navigation property
public Results Results { get; set; }
Remove your configuration instructions. There is no need to use fluent api to configure your database scheme since entity framework will do it by self. Here are examples how to configure one to many relations via code first approach
As was answered in comments it is a good practice to use singular name for a model in order to use plural form for navigational properties.

Creating subtype of a subtype with Table-per-Type (TPT) approach using ef core

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.

One-to-one with different classes in EF Core 2.2

I'd like to follow this blog explaining how to configure one-to-one relationship. Its idea is that one entity gets a property of the other's type while the other one gets a property of the former's type plus an ID to it to create a foreign key.
My issue is, though, that I want to brake out the contact part of two different classes like so. The class SomeThing is already refactored and works well with the class Address. However, I'm not sure how to deal with the class SomeThingElse.
public class SomeThing
{
public Guid Id { get; set; }
//public string Street { get; set; }
//public string City { get; set; }
public Address Address { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public Guid SomeThingId { get; set; }
public SomeThing SomeThing { get; set; }
}
public class SomeThingElse
{
public Guid Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
//public Address Address { get; set; }
}
I've tried adding a specialized class for managing the address of SomeThingElse but then, it makes no sense to break it out. I considered adding the two fields below but rejected the idea as poor design for the DB.
public class Address
{
...
public Guid SomeThingElseId { get; set; }
public SomeThingElse SomeThingElse { get; set; }
}
Preferably, this is a school book case for inheritance introducing a base class Contactable and skipping Address altogether. But I recall from before that inheritance and EF don't mix well and that there's a lot of oopsies and gotchas to be expected in such case.
Is there a reliable best-practice for doing that? I haven't found anything that felt trustable enough when I googled.
As from the discussion in the comments, I am going into a details answer:
You can use EF Core newly introduced Owned Entity type feature where Address is the Owned Entity type of Something and SomethingElse while Something and SomethingElse are the owners as follows:
modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address);
modelBuilder.Entity<SomeThingElse>().OwnsOne(st => st.Address);
By convention, EF Core will name the database columns for the properties of the owned entity type following the pattern Navigation_OwnedEntityProperty. Therefore the Address properties will appear in the Something and SomethingElse table with the names 'Address_Street' and 'Address_City'.
Now if you don't want owned entity type column name to be like Navigation_OwnedEntityProperty then you can give your custom column name as follows:
modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address,
a =>
{
a.Property(p => p.Street).HasColumnName("Street");
a.Property(p => p.City).HasColumnName("City");
});
modelBuilder.Entity<SomeThingElse>().OwnsOne(ste => ste.Address,
a =>
{
a.Property(p => p.Street).HasColumnName("Street");
a.Property(p => p.City).HasColumnName("City");
});
Moreover owned types can be stored in a separate table from the owner. In order to override the convention that maps an owned type to the same table as the owner, you can simply call ToTable and provide a different table name as follows:
modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address,
a =>
{
a.ToTable("SomeThingAddress");
});
modelBuilder.Entity<SomeThingElse>().OwnsOne(ste => ste.Address,
a =>
{
a.ToTable("SomeThingElseAddress");
});
Querying owned types
When querying the owner the owned types will be included by default. It is not necessary to use the Include method, even if the owned types are stored in a separate table.
Limitations
Some of these limitations are fundamental to how owned entity types work, but some others are restrictions that we may be able to remove in future releases:
By-design restrictions:
You cannot create a DbSet<T> for an owned type
You cannot call Entity<T>() with an owned type on ModelBuilder
For more details: EF Core Owned Entity Types Limitations

EF Code First not generating table for ICollection<string>

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

Many-to-many relationships using EF Code First

I have two classes defined as such:
public class Questionnaire
{
public int QuestionnaireID { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
I beleive this is the correct way to establish a many-to-many relationship between these classes, and when the project is built, I would expect three tables to be created.
However, when I attempt to to relate one Questionnaire to two different Vendors, I receive the following error when attempting to save the changes (context.SaveChanges()):
*Multiplicity constraint violated. The role 'Vendor_OpenQuestionnaires_Source' of the relationship 'QuestionnaireApp.Models.Vendor_OpenQuestionnaires' has multiplicity 1 or 0..1.*
If I assign a Questionnaire to only one Vendor, save the changes and then assign it to another and again save changes I no longer get the error; however the Questionaire is then related only to the last Vendor to which it was assigned, indicating that (at best) there is a one-to-many relationship being created.
I'm hoping that there is something wrong with the way I'm declaring the many-to-many relationship between these classes, or perhaps there is something I need to add to the context class to "encourage" the relationsip, but perhaps many-to-many relationships like this are not supported, or cannot be created using "Code First"?
Thank you for your time,
Jason
If you don't have any Fluent API code your expected mapping relies on EF Code First conventions. The convention which you expect to kick in here is the AssociationInverseDiscoveryConvention. Now if you look in Intellisense (and probably also documentation) it says about this convention:
Convention to detect navigation properties to be inverses of each
other when only one pair of navigation properties exists between the
related types.
Now, that's the problem: You don't have only "one pair" of navigation properties between Questionnaire and Vendor. You have two collections in Vendor refering to Questionnaire and one collection in Questionnaire refering to Vendor. The result is that this convention doesn't get applied and EF maps actually three one-to-many relationships with only one end exposed as navigation property in the model.
Moreover the mapping you want to achieve is not possible with your model: You cannot map the one end Questionnaire.Vendors to the two ends Vendor.OpenQuestionnaires and Vendor.SubmittedQuestionnaires.
One workaround is to change your model the following way:
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
[NotMapped]
public IEnumerable<Questionnaire> OpenQuestionnaires
{
get { return Questionnaires.Where(q => q.IsActive); }
}
[NotMapped]
public IEnumerable<Questionnaire> SubmittedQuestionnaires
{
get { return Questionnaires.Where(q => !q.IsActive); }
}
public virtual ICollection<Questionnaire> Questionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
Now Vendor.Questionnaires is mapped to Questionnaire.Vendors (AssociationInverseDiscoveryConvention should detect this) and the helper properties OpenQuestionnaires and SubmittedQuestionnaires allow you to pull out the selected items. (I'm not sure if IsActive is your distinguishing flag. Otherwise you have to introduce some new flag.)
The [NotMapped] attribute is just here to make it explicite. It is probably not necessary because EF won't map IEnumerable collections and readonly properties with only a getter anyway.
Go figure, after an hour or so of searching, I go and find the exact answer 30 seconds after I post my question.
The solution was to add the following to the context class:
modelBuilder.Entity<Vendor>()
.HasMany<Questionnaire>(x => x.OpenQuestionnaires)
.WithMany(x => x.Vendors)
.Map(x =>
{
x.MapLeftKey("vID");
x.MapRightKey("qID");
x.ToTable("VendorQuestionnaires");
});
I found the answer by reading this Stack Overflow post: EF Code First Many-to-Many not working

Categories

Resources