Adding attributes to EF6 generated entity classes [duplicate] - c#

This question already has answers here:
Using DataAnnotations with Entity Framework
(2 answers)
Closed 6 years ago.
I am using EF6 in a database first context. In this case I am using the entity classes in my MVC web project and would like to annotate the entity's fields with various validation and display attributes. However, when I refresh the entity classes by doing an update from database in my edmx designer, the classes regenerate and my attributes are lost.
What is the best approach to getting round this?

When working with generated entity classes in a database first Entity Framework project, it is often necessary to apply attributes to the class’s fields. This is especially the case if you are foregoing the use of ViewModels and using your entities directly in an MVC web project.
Of course if you were to apply validation or display name attributes to the fields directly, the next time the data model is generated due to an upgrade from database action, these would all be overwritten.
Luckily the classes generated by Entity Framework are marked as partial. This means that we can create a second class that augments the first. Effectively the two classes are seen as one. For example:
[MetadataType(typeof(AnimalMetaData))]
public partial class Animal
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int NumberOfLegs { get; set; } etc..
public class AnimalMetaData
{
[MaxLength(200)]
[Required]
public string Name { get; set; }
[MaxLength(1000)]
public string Description { get; set; } etc...
But of course we have a problem here. We have put the attribute to associate the metadata class on the entity class and this will be overwritten on an update from the database. So how do we get round this? Simple! We create a third ‘dummy’ class called Animal that sits alongside the metadata class and is also partial:
[MetadataType(typeof(AnimalMetaData))]
public partial class Animal {}
We annotate this class with our metadata class, so in effect we have the three classes acting as one; the entity itself, the dummy class to link the entity with the metadata definition class.
For the sake of keeping things tidy, you could do worse than to place the metadata and dummy classes together in a separate folder adjacent to the entities generated by Entity Framework.

Related

C# Entity Framework Adding New Column to Entity that's from external library

On a project I am working on I am having some troubles trying to add properties to an entity from an external NuGet package.
The external team originally used the EntityFramework to create their database, and awhile back my team used it initially in order to create ours, now having two separate databases but initial creation used the common NuGet package.
On the external team's side, they haven't changed the table at at all, but on our side we've added new columns and properties to our database and now we need it within our DBContext. How do I map these new fields to an Entity so that I can access and set the properties. I hoped it was protected but since it is public I can't just overwrite the DbSet<Profile> Profile call.
External Package:
DataContext (Class that extends DBContext and has a public DbSet<Profile> Profile {get;set;})
Profile (Entity that is mapped to the "Profile" table in the database)
Since I can't modify the Profile class, how do I go about adding new columns that are there in the table?
My initial approach was to create:
DataContextExt (class that extends DataContext and added public DbSet<ProfileExt> ProfileExt {get;set;}
ProfileExt (Entity that extends Profile and has the additional fields that aren't part of the original
This seems to get me the furthest, but since ProfileExt extends Profile, I get an error when using it due to the "Discriminator" column since they are both the same entity technically.
I then tried to remove Profile by overriding the OnModelCreating() and map my ProfileExt to Profile but that failed as well, it didn't seem to change the model builder at all.
public class DataContextExt : DataContext
{
public DbSet<ProfileExt> ProfileExt { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Ignore<Profile>();
modelBuilder.Entity<ProfileExt>().ToTable("Profile");
Database.SetInitializer<ApplicationDbContext>(null);
}
}
Does anyone have any suggestions on what I should try next going forward?
EDIT:
Currently the project is design to access the information VIA a Stored Procedure and then I mapped that to my ProfileExt, but when it comes to saving it is designed to use
Entity = await DB.Set<TModel>().FindAsync(Key.Compile()(Model)).ConfigureAwait(false);
Model is instance of ProfileExt when it reaches this point
If I try to pass a ProfileExt through(without its own DbSet) as Profile it fails saying ProfileExt is not in the context, and if I do register it (with its own DbSet) it throws the Discriminator error since once is an instance of another.
From the sounds of things you are using a library and initial schema provided by some third party. You don't share code modifications with that team but you've gone and changed part of their schema in your copy of this Profile table.
Why not keep the two DbContexts completely separate rather than trying to inherit to override?
One option would be not to add columns to a table/entity that you do not have ownership of to extend. Move your custom columns to something like a MasterProfile table which shares a ProfileId as it's PK. From there you can declare a MasterProfile entity with a one-to-one relationship with Profile.
public class MasterProfile
{
[Key]
public int ProfileId { get; set; }
// add custom columns here...
public virtual Profile Profile { get; set; }
}
then configure relationship:
modelBuilder.Entity<MasterProfile>()
.HasRquired(x => x.Profile)
.WithOptional();
This way you can read your custom object in your DbContext along with Profile without a breaking change to the schema.
Another option you could explore is to define your own Profile entity definition for your DbContext while reusing the other entity declarations from the 3rd party.
For example: Given a 3rd party library which defines the following classes:
3rdParty.User
3rdParty.Profile
3rdParty.TableA
3rdParty.TableB
3rdParty.TableC
and they are accessed by a 3rdParty.DataContext
I can define a MyApp.DataContext that does not need to extend 3rdParty.DataContext. That DbContext can reference a Profiles collection that is declared as:
MyApp.Profile
which contains the full set properties from our Profile table. Provided you don't have to worry about references to 3rdParty.Profile you don't need to create custom entities for every table, you can reference 3rdParty.TableA etc. in your MyApp.DataContext.
I.e.
public class DataContext : DbContext
{
public DbSet<Profile> Profiles { get; set; }
public DbSet<3rdParty.TableA> TableAs { get; set; }
public DbSet<3rdParty.TableB> TableBs { get; set; }
public DbSet<3rdParty.TableC> TableCs { get; set; }
}
The catch would be that this will only work if the class you define is not referenced by many other entities. Every 3rdParty entity definition we include in our DbContext can no longer reference a 3rdParty.Profile since our DbContext cannot have two entities mapped to the same table.
For instance, if Profile references a User, that isn't a problem since MyApp.Profile can reference 3rdParty.User, however if 3rdParty.User has a reference back like:
public virtual ICollection<Profiles> { get; set; }
which will be pointing back to the Profile in the 3rdParty assembly, this is a deal breaker. We will need to recreate a MyApp.User as well. This could cascade if something like User needs to be re-declared and that class is referenced by the majority of other entities. (I.e. public virtual User CreatedBy { get; set; })
It may be an option worth exploring.

Entity Framework 6 throws exception for missing SQL table not required by DbContext

I have created a DbContext, similar to the one below
public class ProductsDB : DbContext
{
public virtual DbSet<Product> Products { get; set; }
//..other stuff..
}
Here's Product;
[Table("product")]
public class Product
{
[Column("Product_ID")]
public int ID { get; set; }
//other fields...
}
This all works fine until I create a class that inherits from "Product";
[Table("CentralProducts")]
public class CentralProduct : Product
{
//fields...
}
When I run integration tests I get an error that states that dbo.CentralProducts doesn't exist. It doesn't, and for the database that ProductsDB is connecting to I don't want it to.
I have tried the various inheritance options but they either require the addition of a table to my database, or the addition of a discriminator column to the Product table.
I had hoped that since my context doesn't consume CentralProducts it would not require it to exist. I was using table-per-type inheritance so there shouldn't be any need for something consuming dbo.Product to be able to access dbo.CentralProduct. Is there a way to configure EF6 to allow for this?
The reason I am trying to do this is that I have two databases, each with a common set of tables. I would like to create two DbContexts, one for each database, each containing the core entities but also including a few differences. In this particular case, the second DbContext has a Product table that includes an additional field, hence my attempt at inheritance to solve the problem.

What is a proper way of writing entity POCO classes in Entity Framework Core?

EF Core has a "code first mentality" by default, i.e. it is supposed to be used in a code-first manner, and even though database-first approach is supported, it is described as nothing more than reverse-engineering the existing database and creating code-first representation of it. What I mean is, the model (POCO classes) created in code "by hand" (code-first), and generated from the database (by Scaffold-DbContext command), should be identical.
Surprisingly, official EF Core docs demonstrate significant differences. Here is an example of creating the model in code: https://ef.readthedocs.io/en/latest/platforms/aspnetcore/new-db.html And here is the example of reverse-engineering it from existing database: https://ef.readthedocs.io/en/latest/platforms/aspnetcore/existing-db.html
This is the entity class in first case:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
and this is the entity class in second case:
public partial class Blog
{
public Blog()
{
Post = new HashSet<Post>();
}
public int BlogId { get; set; }
public string Url { get; set; }
public virtual ICollection<Post> Post { get; set; }
}
The first example is a very simple, quite obvious POCO class. It is shown everywhere in the documentation (except for the examples generated from database). The second example though, has some additions:
Class is declared partial (even though there's nowhere to be seen another partial definition of it).
Navigation property is of type ICollection< T >, instead of just List< T >.
Navigation property is initialized to new HashSet< T >() in the constructor. There is no such initialization in code-first example.
Navigation property is declared virtual.
DbSet members in a generated context class are also virtual.
I've tried scaffolding the model from database (latest tooling as of this writing) and it generates entities exactly as shown, so this is not an outdated documentation issue. So the official tooling generates different code, and the official documentation suggests writing different (trivial) code - without partial class, virtual members, construction initialization, etc.
My question is, trying to build the model in code, how should I write my code? I like using ICollection instead of List because it is more generic, but other than that, I'm not sure whether I need to follow docs, or MS tools? Do I need to declare them as virtual? Do I need to initialize them in a constructor? etc...
I know from the old EF times that virtual navigation properties allow lazy loading, but it is not even supported (yet) in EF Core, and I don't know of any other uses. Maybe it affects performance? Maybe tools try to generate future-proof code, so that when lazy-loading will be implemented, the POCO classes and context will be able to support it? If so, can I ditch them as I don't need lazy loading (all data querying is encapsulated in a repo)?
Shortly, please help me understand why is the difference, and which style should I use when building the model in code?
I try to give a short answer to each point you mentioned
partial classes are specially useful for tool-generated code. Suppose you want to implement a model-only derived property. For code first, you would just do it, wherever you want. For database first, the class file will be re-written if you update your model. So if you want to keep your extension code, you want to place it in a different file outside the managed model - this is where partial helps you to extend the class without tweaking the auto-generated code by hand.
ICollection is definitely a suitable choice, even for code first. Your database probably won't support a defined order anyway without a sorting statement.
Constructor initialization is a convenience at least... suppose you have either an empty collection database-wise or you didn't load the property at all. Without the constructor you have to handle null cases explicitely at arbitrary points in code. Whether you should go with List or HashSet is something I can't answer right now.
virtual enables proxy creation for the database entities, which can help with two things: Lazy Loading as you already mentioned and change tracking. A proxy object can track changes to virtual properties immediately with the setter, while normal objects in the context need to be inspected on SaveChanges. In some cases, this might be more efficient (not generally).
virtual IDbSet context entries allow easier design of testing-mockup contexts for unit tests. Other use cases might also exist.

Force Entity Framework to recognise a TPT Inheritance Structure

Given an example structure as below, Entity Framework doesn't recognise the base Entity inheritance, and thus doesn't map it as a TPT Inheritance between User and Entity.
// Where DbSet<User> Users { get; set; } is used
public class User : User<int> { }
public class User<TTest> : Entity {
public TTest Whatever { get; set; }
}
public abstract class Entity {
public int EntityId { get; set; }
}
I believe this is because EF will only look at the first level inheritance structure and never see the Entity as the base class, just that it has its properties.
So my question is, how can I force EF to recognise that Entity is indeed the base class? Just defining that User has a ForeignKey to Entity in the migration obviously isn't enough, as it still doesn't create that underlying row.
Bonus points: I've already noted that I can't go another level down (ie. Employee : User), but if you'd like to correct me on that I'll be forever in your debt also.
Update: Repro available here on github.com.
Update2: Gert Arnold's theory about not being able to map generic classes and thus breaking the chain unfortunately generated the same migration wherein User didn't fall through to Entity.
Update3: I can confirm that the stricken out "bonus" above does indeed work, when User maps correctly. The inheritance structure of Entity : User : Employee works when all 3 are tables, it is obviously not working when User can't even map back to Entity, which I believe now to be a bug in EF.
You need to add a DbSet for each type that you want EntityFramework to add a table for, like this:
public virtual DbSet<Entity> Entities { get; set; }
public virtual DbSet<User> Users { get; set; }

How do I add multiple tables using one model and one view?

I want to have a page where the user selects from a drop down list the category, then adds a small text about that category and uploads an image where the path of that image is saved in the database rather than the whole image. I have created a table "Categories" where the admin is authorized to fill it and the user only selects from the categories list.
Here is what I have done so far:
The create categories model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace DemoIdentity.Models
{
public class CategoriesAdmin
{
public int ID { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "category name")]
public string categoryName { get; set; }
}
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> categories { get; set; }
}
}
Now I want to have another table (Data) which includes (ID, Category (category name selected from table categories), News, Image_Path). This table is in the Default Connection database. The category name is the selected category name from a drop down list, and the image path is an upload image which saves the path rather than the whole image.
I am unsure of how to achieve this.
It appears that you are confusing components of ASP.NET MVC and Entity Framework.
As the Entity Framework site states:
Entity Framework (EF) is an object-relational mapper that enables .NET
developers to work with relational data using domain-specific objects.
It eliminates the need for most of the data-access code that
developers usually need to write.
And the MVC site states that:
The ASP.NET MVC is an open source web application framework that
implements the model–view–controller (MVC) pattern.
The two frameworks meet through your model classes. MVC uses the model class to define the data, logic and rules of the application. In Entity Framework, your model class is mapped to tables in your database where it handles the direct reads and writes for you.
By creating your CategoriesAdmin model class and exposing it as a property in your DbContext class as such:
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> categories { get; set; }
}
Entity Framework will have mapped your model class to a database table called CategoriesAdmins. If this table does not yet exist in your database, it will automatically create it for you. This approach in Entity Framework is known as Code First to a new Database.
Now since you already have a table that stores the available categories (CategoriesAdmin), you will need to create a second model class (called Data for the sake your example) which contains properties for the other bits of information that you want to store.
public class Data
{
// gets or sets the ID of this Data record.
public int ID {get;set;}
public string ImagePath {get;set;}
// other properties
...
}
Now that you have two model classes, you need to create a relationship between the two. In a SQL database this is achieved by Foreign Keys. In Entity Framework, you can achieve the same by using Navigational Properties.
So we update the Data model class as such:
public class Data
{
// gets or sets the ID of this Data record.
public int ID {get;set;}
public string ImagePath {get;set;}
// gets or sets the ID of the related CategoriesAdmin record.
public int CategoriesAdminId {get;set;}
// gets or sets the related CategoriesAdmin record. Entity Framework will
// automatically populate this property with an object for the related
// CategoriesAdmin record.
[ForeignKey("CategoriesAdminId")]
public virtual CategoriesAdmin CategoriesAdmin {get;set;}
// other properties
...
}
The ForeignKeyAttribute on the CategoriesAdmin property is there to give Entity Framework a further hint of the foreign key column to load the navigational property from.
Finally to be able to use your new Data model class with Entity Framework, you need to add another property to your DbContext class so that you have a means of accessing your data:
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> Categories { get; set; }
public DbSet<Data> Data { get; set; }
}
Now that you have created your model classes and wired them into Entity Framework, you will now be able to use them in MVC. If you load your Data model into your view (using DefaultConnection.Data), you will be able to access the related CategoriesAdmin record by accessing the CategoriesAdmin property on the Data object.
In short: two tables means you need two models. Both models can be loaded into the single view.
Footnote: Apologies if there are large gaps in my answer as there is a lot to explain that have already been explained in other places far better than what I can. The references I have linked should hopefully fill in the gaps.
Should you need more help, please see all of the tutorials on the ASP.NET MVC website on working with data. They're much better written than my concise attempt. I would recommend following them exactly and getting the examples to work before completing your own project so that you have a better understanding of how the two frameworks work and interact with each other.

Categories

Resources