Adding ViewModel class adds codefirst model to database - c#

I need a view-model in my ASP.NET MVC 5 project, but when I added one to the models folder, a new entity was added to the database and I was forced to add a migration and update my database. I do not want this to happen, as it is a view-model I am adding and not a model I need to persist back to the database. I want to scaffold some of the controller and views so I have added a primary key to the class. I did not add the newly created view-model to my DbContext class.
ViewModel:
public class RolesViewModel
{
public int RolesViewModelId { get; set; }
public string Role { get; set; }
}
Is there a way to create a view-model that doesn't automatically get added to the DbContext class, and therefore cause the data model to change?
Many thanks,
Jason.

Whether you call it a view model, an entity, etc. it's just semantics. Everything is just a class, and the context it's used in determines what you refer to it as. In the case of entities, that's adding a reference either explicitly or implicitly in your DbContext, and that's the only way you'll end up with something added to your database. I emphasized the "or implicitly* part because if any class that is referenced in the your DbContext, or any class connected to any class referenced there, also references your "view model", it will end up in your database. Entity Framework will automatically follow your class hierarchies and create tables for all relationships, even if you do not reference a particular class in those hierarchies directly in your DbContext.

In your case Scaffolding will add the following code below in your appContext Class
public DbSet<RolesViewModel> RolesViewModel { get; set; }
You can still use scaffolding if you wish, however remember to remove that entry and no table will be created by code first. It will keep your database clean.

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.

EF6 Code First Migration with single database multiple context

I'm building an Asp.net MVC5 + EF6 solution with 3 projects.
I have enabled automatic migration in my project.
The below diagram shows my project structure.
I have a main project and two sub projects.
I have a BaseContext in a main project.
Sub project has their own context classes which derives from
BaseContext.
All above contexts are connecting to one database.
Models:
A Model in Project2
public class Product
{
[Key]
public int ProductId {get;set;}
...
}
A Model in Project3
public class Order
{
[Key]
public int OrderId {get;set;}
[ForeignKey("Product")]
public int ProductId {get;set}
public virtual Product Product;
...
}
An property from Project3 entity (Order.ProductId) references a property from Project2 entity (Product.ProductId) as a foreign key reference.
When I run update-databasecommand in project 1 & 2 everything is going well.
But when run update-database command in project 3 It gives an error:
There is already an object named 'Product' in the database.
Right now I'm using update-database -script command to generate script and manually altering the script. But when project grows, it becomes a difficult task to alter sql scripts each and every time.
I ignored the Product entity by adding modelBuilder.Ignore<Product>() inorder to skip table creation for Productentity, in Project3, but it's ignores the entire relationship.
How can I solve this issue?
You can't do this. One context == one database. When you run a migration against a database for a particular context, it will be based on that context, and will remove any non-applicable tables or try to drop and recreate the database.
You should move all your entities into a class library, along with a single instance of a context that includes them all. Then, you can migrate against the class library and simply reference in your other projects.
Alternatively, you can sort of do what you want by going with an existing database approach. Essentially, you turn off migrations entirely on your base context, and then each sub context can then only include the entities that belong to it. However, you're going to be completely responsible for managing the database, yourself, at that point.
public class BaseContext : DbContext
{
public BaseContext()
: base("ConnectionString")
{
Database.SetInitializer<BaseContext>(null);
}
}

Bind database to Collection c#

I am developing a data intensive app in C# and need some advice on the best method to manage my data. My application will use a number of related tables with a lot of movement backwards and forwards between the app and the database.
My question is, what method would be best to handle data? All of my data fields are controlled within their own classes and collections, so hence, the question is really more of a case of what is the best method to bind my collections and objects to the database.
Should I manually create my own SQL connections / SQL Insert / Update queries, or is there another way to do this? For instance is it possible in Visual Studio to easily create a strongly typed data layer that will automatically look after Inserts / Updates / Deletes etc?
Many thanks
Q. For instance is it possible in Visual Studio to easily create a strongly typed data layer that will automatically look after Inserts / Updates / Deletes etc?
Entity Framework sounds like something you're looking for. There is, of course, some setup involved. With most web projects, Entity Framework is automatically added, and if not you can always add it via NuGet package manager.
EntityFramework binds your models to a Database through the DbContext object, and Code First will even generate a database if one does not exist based on your models (if one does exist and the models change, you might have to migrate: More reading). I will be taking that approach (you can later change the following code to a connection string to a live DB).
First, you need to tell your application what database to use (whether it exists or not), and this can be done in the Web.Config file (not the only approach).
Web.Config
<configuration>
....
<connectionStrings>
<add name="ConnectionStringName" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\DATABASENAME.mdf;Initial Catalog=DATABASENAME;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
NOTE - this connection string will only work in VS2015. For previous versions, use:
Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\DATABASENAME.mdf
More reading on configuring Entity Framework
Now, we can use "ConnectionStringName" to connect our context.
The Context class will have to inherit from the DbContext object, and we will pass this connection string name to the base constructor (DbContext exists in the System.Data.Entity namespace). I will use two generic models that look identical (ModelA ModelB) --
ModelA and ModelB
using System.ComponentModels.DataAnnotations;
public class ModelA
{
public int Id { get; set; }
[Required(ErrorMessage="You need to enter a name!")]
[StringLength(40)]
public string Name { get; set; }
}
NOTE - I'm using DataAnnotations (Required, StringLength) - these are really cool and can be very useful when implementing CRUD operations on your model.
ALSO -- Entity Framework will automatically make properties that are named: Id (or any case form) or ModelNameId the primary key. There are ways to override this.
More reading on DataAnnotations
You can even create your models with relationships in mind. If ModelA will have a one-to-many relationship with ModelB, you can reflect this relationship in code:
public virtual ICollection<ModelB> ModelBs {get; set;}
More reading on creating relationships
We will now map your models to tables using the DbSet collection. Here's how that looks:
using System.Data.Entity;
using YourProject.Data.Models;
namespace YourProject.Data.DAL
{
public class YourContext : DbContext
{
public YourContext() : base("ConnectionStringName")
{
}
public DbSet<ModelA> ModelAs { get; set; }
public DbSet<ModelB> ModelBs { get; set; }
}
}
Database Initialization Strategies - for seeding your database with data on creation.
More reading on configuring the context
I will never be able to detail everything you can do - but here are a few short examples:
Q. What does all this do?
You now have a strongly-typed object connected to your database. It's as simple as creating a new Context object:
YourContext yourContext = new YourContext();
Retrieving a whole table is as easy as:
IEnumerable<ModelA> modelAs = yourContext.ModelAs;
or finding a row by Id
ModelB modelB = yourContext.ModelBs.Find(id);
Say you grabbed modelB and want to update it. It's as easy as:
modelB.Name = "Here's the new name";
yourContext.Entry(modelB).State = EntityState.Modified;
yourContext.SaveChanges();
Adding:
yourContext.ModelAs.Add(newModel);
Removing:
yourContext.ModelBs.Remove(modelB);
REMEMBER Add/Update/Delete methods will ALL require a call to SaveChanges(); from the Context. Otherwise they will be discarded.
Of course, this is probably the most basic rundown of Entity Framework available. To see more of it in action check out these tutorials:
WebForms with Entity Framework - Wingtip Toys
MVC with Entity Framework - Contoso University
Hope this may be of assistance for someone.

Forcing EF 6 to create tables with model first

Currently I have a nice model, and I can generate a database based on that, but from what I can tell, the tables are never created (leading to all sorts of fun runtime errors).
My understanding is that there are three options for code first that would force EF to create the tables for me:
DropCreateDatabaseAlways
CreateDatabaseIfNotExists
DropCreateDatabaseIfModelChanges
How can I use these if I am doing things model first?
Additionally, is this an expected error, or when I selected generate database from model the first time is this supposed to happen automatically?
Edit: I tried calling
context.Database.Initialize(true);
context.Database.CreateIfNotExists();
and nothing changes.
also this is good toturial
tutorial
but if you made the model good the first time you access the dbContext the db should be created by the db strategy which you can set: Database.SetInitializer()
set initializer
in short after you create your model you need to create class that inherit from DbContext:
public class CompanyContext : DbContext
{
public CompanyContext() : base("CompanyDatabase") { }
public DbSet<Collaborator> Collaborators { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Manager> Managers { get; set; }
}
and then when you access this context the tables should be generated.
you can also seed the database with data you should inherit from the strategy you want to implement look at this link seeding database

One Context class, two Model classes

I'm learning MVC, and I understand how to create a single model class, and then a single context class, and then create a controller and a view based on the model/context combination, and commit the model to a database using code-first.
Now I want to bring a second Model (and therefore table) into the mix. I initially tried creating a second context class, and then a second model class, and then a controller and a view in the same way. As a result of this I found out that I could only commit one model to the database at a time. Whenever I enabled automatic migration on one model, and then ran the Update-Database command in the package manager, the other table disappeared from my database, and the add/remove/edit/view functionality for the other model broke.
Surely you must be able to use this techinque to manage more than one table? My question is, if I have two model classes like below:
[Table("TableOne")]
public class ModelOne
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Foo { get; set; }
}
[Table("TableTwo")]
public class ModelTwo
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Bar { get; set; }
}
What does my Context class need to look like if I want to work with both? My idea was to try this:
public class FooBar : DbContext
{
public FooBar()
: base("DefaultConnection")
{
}
public DbSet<ModelOne> TableOne { get; set; }
public DbSet<ModelTwo> TableTwo { get; set; }
}
Then in the Add Controller wizard, I could select MVC controller with read/write actions and views, using Entity Framework from the Template drop-down list, ModelOne from the Model class drop-down list, and FooBar from the Data context class drop-down list for the first model, and the same options only with ModelTwo in the Model class drop-down list for the second model and everything would work as expected, but whenever I try to create the first controller based on model one, I get this error:
There was an error generating FooBar. Try rebuilding your project.
I have tried rebuilding my project, and I get the same error, so obviously I am doing something greater wrong. What am I doing wrong and how do I need to amend my FooBar context class to get things to work, assuming it is my FooBar context class which is wrong?
Generally, you won't have a 1-to-1 between context classes and model classes. All your associated models all go into one context. I generally have one context per area in my app, for instance, but if you aren't using areas, you can still group models logically in a single context.
Where the problem comes in is in having multiple contexts. EF only likes to migrate one context. Without migrations, you can pretty easily get all the context classes to use the same DB by specifying it manually on the context class.
public class MyContext: DbContext
{
public MyContext()
: base("name=MyConnectionName")
{
}
}
If each context is defined like that, they will all use the same DB the named connection string points to.
As far as getting migrations to play along, Julie Lerman, the author of Programming Entity Framework: DbContext (and pretty much all the relevant EF books out there), has a solution utilizing a generic base class (edit: actually Julie attributes the pattern to Arthur Vickers of the EF team):
public class BaseContext<TContext> : DbContext
where TContext : DbContext
{
static BaseContext()
{
Database.SetInitializer<TContext>(null);
}
protected BaseContext()
: base("name=breakaway")
{
}
}
And, then you have each individual context inherit from BaseContext:
public class TripPlanningContext : BaseContext<TripPlanningContext>
public class SalesContext : BaseContext<SalesContext>
The relevant section starts on page 231 of the aforementioned book.
UPDATE
Actually I lied to you. The pattern Julie gives is simply to make it easier to have them all share the same DB. To get past the migrations for only one context issue, you have to cheat and essentially create one big context with everything in it that you then use only for database initialization/migration. For everything else in your code, you would use your other more specific contexts.

Categories

Resources