Maybe I haven't quite get the whole thing about models. I tought that, it was probably wrong, EF framework could map any kind of class. So I did provide classes with different interfaces, with ToString() methods and so on.
I was thinking of reusable/flexible structure of classes for some kind of public organization.
For example, there are next classes
[Serializable]
public abstract class AbstractRole
{
public String Title { get; set; }
public abstract void ExecuteRole();
public abstract Decimal GetSalary();
// ToString(...) implementations and so on
}
[Serializable]
public class Employee<T> : IComparable<Employee<T>>, IFormattable where T : AbstractRole
{
private Person person;
public T Role { get; set; }
// interfaces implementations...
}
So all I wanted is to get flexibility to change employee's role in time and not to bind to its instance (avoid inheritance).
But later I read that generics are not supported by EF.
What should I do?
In case of entity framework you must provide exact type. Base classes are supported only if whole inheritance tree is mapped as well. Interfaces and generic types are not supported at all.
That means that reusable and flexible architecture is not something which can use EF.
Related
I have a class called GatewayClaims and a class called GatewayItems. And yes, the project I'm working on is a gateway.
I have several classes derived from GatewayItems: GatewayUser, GatewayCompany, GatewayRole and a few more. Each of these derived classes will hold claims. (Actually, just values. Simplified here.) And these claims gets passed forward to another service as a JWT token. This should work just fine.
But the problem is this:
public class GatewayClaim
{
public GatewayItem Item { get; set; } = new();
}
public abstract class GatewayItem
{
public List<GatewayClaim> Claims { get; set; } = new();
}
The "abstract" is part of the challenge here...
The problem is that I want separate tables for each item/claim pair so I have UserItems/UserClaims, CompanyItems/CompanyClaims, etc. So, preferably I would make the Claims type a generic class GatewayClaim<T> where T:GatewayItem, new() but then List<GatewayClaim> becomes invalid. And I don't weant to create a lot of derived classes just to support the various configurations that would be possible. I could use List<GatewayClaimValue<GatewayItem>> in GatewayItem which seems to work. But then I need to configure the DBSet and IEntityTypeConfiguration class for the various Claims tables and things become really messy by then.
So, I'm looking for an elegant solution to keep the amount of code to a minimum. And keep it readable!
To be clear: GatewayItem is NOT directly mapped to an entity, but a public class GatewayItemConfiguration<T> : IEntityTypeConfiguration<T> where T : GatewayItem is used to allow inheritance of basic configuration for any derived classes. This has an public virtual void Configure(EntityTypeBuilder<T> builder) method that gets overridden in the child configuration classes. Again, I'm trying to stay DRY in my code.
So the GatewayUser class uses a public class GatewayUserConfiguration : GatewayItemConfiguration<GatewayUser> {} class to configure the GatewayUser entity. I do the same way for a GatewayUserClaim which is derived from GatewayClaim at this moment. But the derived Claim types don't differ from their parent class, except the Items list is of a different type. Which is why I want to use GatewayClient<T> instead of GatewayClient.
I have several classes derived from GatewayItems: GatewayUser, GatewayCompany, GatewayRole
These are not closely-enough related to use inheritance in the database. If you want to have a common base class in code, simply don't map GatewayItem to an EF entity.
I want separate tables for each item/claim pair so I have UserItems/UserClaims
Great. Just introduce a UserClaim type, again perhaps inheriting from an unmapped Claim type, and it will map to a separate UserClaim table.
I'm starting to use EFCore in a UWP app with SQLite. I've declared the classes that I want stored in the database. I've then declared various DbSets and the OnConfiguring override:
public class DatabaseContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Relationship> Relationships { get; set; }
public DbSet<SourceDescription> SourceDescriptions { get; set; }
public DbSet<Agent> Agents { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<Document> Documents { get; set; }
public DbSet<PlaceDescription> PlaceDescriptions { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=Testing.db");
}
}
In the Package Manager Console, I then go:
Add-Migration 2017-06-23
and it creates the code to then build the database. HOWEVER, that code is missing some of the top-level tables. Some of them are there but some aren't and I can't see/figure out what is causing this. No errors are generated and I cannot find a pattern to determine why the missing tables are missing.
Further information:
There are 29 classes defined in my code. The migration code creates 24 tables.
Of the 5 "missing" classes, 4 of them are derived from Subject which, in turn, derives from Conclusion. The fifth is the Conclusion class. Since the migration code "merges" the properties from each of the derived classes, I'm not surprised that the Conclusion class doesn't have its own table as there is no direct use of it as a class.
With the exception of the Conclusion class, the other 4 classes are only referenced in the DbSet properties (as opposed to be used in properties in other classes). As noted above, the Document class is derived from another class, so it isn't derived classes that is the common factor.
It isn't that some of the classes are partial classes.
I can't spot anything about the properties in the classes that is a common factor. For example, the Person class has List a couple of times but so does the Agent class and that does appear in the migration code.
Any suggestions on what I can look for or if this is a known issue?
To clarify, the answer provided by Ivan is that EF Core sees the four classes that are derived from the Subject class and builds a table for the Subject class that includes all of the properties for those derived classes. There is then a Discriminator column added to the Subject class that indicates which derived class this is for.
http://www.learnentityframeworkcore.com/inheritance/table-per-hierarchy is a good article for explaining it.
An interesting approach to designing database tables that I hadn't come across before. I live and learn :)
I am playing around building some buildingblocks based on database tables.
So I've created an UsersManager and a ValidationManager both based on the EDMX "templates".
I'd really like to loose couple those two components with MEF. But therefore i need to create Interfaces of the entityobjects exposed in the ValidationManager.
Is there an easy way of creating those Interfaces, in that manner i can still use the EDMX generated classes?
Thanx,
Paul
Using an example of a database with a Product Table, is this what you're trying to achieve....
but still use generated entity classes (using either the standard EF generator or another POCO generator of some sort).
I'm not sure - as you mention MEF and I don't see it being directly related.
The generated entity classes are partial classes which will allow you to extend the generated class which in this case you want to extend to implement an interface.
Presuming the following interface is going to be used to introduce the layer of abstraction...
public interface IEntity {
public bool IsDeleted { get; set; }
}
Create a new class file with and extended Product class...
public partial class Product : IEntity {
public bool IsDeleted {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
}
You have now extended your generated entity Product with the partial class custom code - and you can use it as normal through EF.
Now instead of your UserManager and ValidationManager classes having a hard reference to Product, instead they'll only have reference to IEntity.
If I didn't understand the question, please provide more details on exactly it is you want to do.
Is it possible to have Code First data classes declared with internal access as shown:
internal class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
I have a requirement that classes and its properties should not be visible outside of the assembly.
As long as your DbContext derived class that exposes your class to EF is in the same assembly, you should be able to. I don't happen to design my apps that way as I prefer more separation. But the context should be able to build the model and it should be able to interact with the classes (e.g. execute queries, save changes etc) if they are in the same assembly since it will have access to the internal class. Even with the various odd things we tried and wrote about in the Code First book, I never happened to try this particular scenario.
I'm new to MVC and I'm trying to figure out how to implement business logic in the auto generated Entities in an MVC project.
I know that if I create my own Model class I can put [Required] and other attributes on the fields but there doesn't seem to be an option to do that in the .edmx file.
Is there something I'm missing here?
Should I be creating my own classes that use the entities and put the logic in there?
It seems like there should be a way for me to do less work.
Thanks!
This can be achieved by using the buddy-class functionality in .NET implemented specifically for this reason. Once you have created your entities in your .ebmx file you can create partial classes to accompany your entities which define your business rules in a 'buddy class'.
[MetadataType(typeof(ProductMetadata))]
public partial class Product {
internal sealed class ProductMetadata {
[DisplayName("Name")]
[Required]
public string Name { get; set; }
[DispayName("Price")]
[Required, Range(1,10000)]
public decimal Price { get; set; }
[DisplayName("Description")]
public string Description { get; set; }
}
}
In the example above, assume that you already have a "Product" type defined in your object context which has properties for "Name", "Price", and "Description". So long as the buddy class type referenced by the MetadataTypeAttribute has matching property names, the attributes applied to the properties in the buddy class will be applied to the implementation type.
Note: if there are any property names in the buddy class which do not match the implementation type, you will get a runtime error. You only need to create matching properties in the buddy class for the properties you are interested in applying business rules to; all properties are optional.