Can EF Code First have two more DBContext in one App? - asp.net-mvc

I have an ASP.NET MVC App, which use EF code First, for some reason I need to use 2 differents DBContext for my app like the example here:
namespace ESN.Models
{
public class CoreA
{
//..........
}
public class CoreB
{
//..........
}
public class CoreDbContext : DbContext
{
public DbSet<CoreA> CoreA { get; set; }
public DbSet<CoreB> CoreB { get; set; }
}
public class StuffA
{
//..........
}
public class StuffB
{
//..........
}
public class StuffDbContext : DbContext
{
public DbSet<StuffA> StuffA { get; set; }
public DbSet<StuffB> StuffB { get; set; }
}
}
And for the convenient developing I add some code that drop and re-create the database if model has change:
Database.SetInitializer<CoreDbContext>(new DropCreateDatabaseIfModelChanges<CoreDbContext>());
Database.SetInitializer<StuffDbContext>(new DropCreateDatabaseIfModelChanges<StuffDbContext>());
But the issue is they just Create new table for the DbContent that I need to use first, the Core table exist but none of the Stuff!
Thanks for your help!

If you want to use two DbContext types and database initializers each context must use its own database. If you use the same database for both context types you cannot use database initializers and you must maintain the database schema changes manually (you must manually or through some SQL script create database and all necessary tables prior to using the application).

Related

Add new entities to DbContext

I use ASP.NET Core with Identity and want to extend default Db context. If I want to add not linked table I just add a new class:
public partial class Table1
{
public int Id { get; set; }
public string Txt { get; set; }
}
and extend my ApplicationDbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Table1> Table1 { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<Table1>(entity =>
{
entity.ToTable("Table_1");
entity.Property(e => e.Id).HasColumnName("ID");
entity.Property(e => e.Txt)
.IsRequired()
.HasMaxLength(50);
});
}
}
then create a migration and update db. It works. But if I want to add a new table, which linked to table from IdentityDbContext:
public partial class Users
{
public int Id { get; set; }
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual AspNetUser User { get; set; }
}
of course, AspNetUser class does not exist (it's created by IdentityDbContext, as I understand). How to do it correctly?
The class is most likely named ApplicationUser (the default). The table that represents this entity is dbo.AspNetUsers, but that is set by Identity, and has nothing to do with the class name.
FWIW, though, it's a bad idea to create a Users entity, for a number of reasons:
There will undoubtedly be confusion between Users and ApplicationUser, as well as the database tables dbo.Users and dbo.AspNetUsers.
In general, you should name your entities in singular tense, i.e. User, not Users. There's a whole host of reasons for this convention, but suffice to say, it just makes your code better and more readable to stick to singular tense for singular things and plural tense for plural things. For example, a property of type ICollection<User> would be named Users, since it's composed of many User instances.
What you're doing is completely unnecessary. The whole reason for Identity's existence is that Membership (the previous authentication and authorization framework employed by ASP.NET) did not allow you to extend the types involved. Identity changes all this and is 100% extensible in every way. You have full access to all entities involved in the the framework and you can add to them and derive from them. If you want to add additional properties for "users" in your system, just add them to the ApplicationUser class directly.

Code migration unexpectedly tries to rename table

I want to implement a change log as advised in
Dev Express XAF T474899
I am using the security system generated by the XAF new solution wizard
I have defined some business objects to store the change log information.
One of these objects stores a link to the user
public virtual User User { get; set; }
On generating the code migration I am surprised to see the Up() method add the following
RenameTable(name: "dbo.UserRoles", newName: "RoleUsers");
DropPrimaryKey("dbo.RoleUsers");
AddPrimaryKey("dbo.RoleUsers", new[] { "Role_ID", "User_ID" });
On another occasion I found the following in an Up()
RenameTable(name: "dbo.EventResources", newName: "ResourceEvents");
// lots of other stuff
DropPrimaryKey("dbo.ResourceEvents");
AddPrimaryKey("dbo.ResourceEvents", new[] { "Resource_Key", "Event_ID" });
On both occasions the code that creates the entities is a Dev Express libary.
I have cross posted this question to Dev Express Support
The Dev Express business objects are defined in DevExpress.Persistent.BaseImpl.EF;
My DbContext context refers to them as
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
The meta data for Role shows
The meta data for User shows
My own business classes contain
namespace SBD.JobTalk.Module.BusinessObjects
{
[NavigationItem("Configuration")]
[DisplayName("Staff")]
[DefaultProperty("Summary")]
[ImageName("BO_Employee")]
[Table("Staff")]
public class Staff : BasicBo
{
public Staff()
{
Person = new Person();
}
public virtual Person Person { get; set; }
[StringLength(100, ErrorMessage = "The field cannot exceed 100 characters. ")]
[scds.Index("IX_Staff_UserName", 1, IsUnique = true)]
public string UserName { get; set; }
[NotMapped]
public string Summary => $"{Person.FirstName} {Person.LastName}";
//public virtual User User { get; set; }
}
}
public abstract class BasicBo : IXafEntityObject
{
[Browsable(false)]
[Key]
public virtual int Id { get; set; }
public virtual void OnCreated()
{
}
public virtual void OnSaving()
{
}
public virtual void OnLoaded()
{
}
}
If I un-comment the code to have the User property inside Staff, and generate a migration, the migration Up is
public override void Up()
{
RenameTable(name: "dbo.UserRoles", newName: "RoleUsers");
DropPrimaryKey("dbo.RoleUsers");
AddColumn("dbo.Staff", "User_ID", c => c.Int());
AddPrimaryKey("dbo.RoleUsers", new[] { "Role_ID", "User_ID" });
CreateIndex("dbo.Staff", "User_ID");
AddForeignKey("dbo.Staff", "User_ID", "dbo.Users", "ID");
}
[Update]
Interestingly there are more Dev Express tables than I first thought.
The primary keys are Identity.
I think am using Standard Authentication created before Dev Express added the Allow/Deny ability (V16.1)
[Update]
When I create a new project with the above settings, here is the DbContext.
using System;
using System.Data;
using System.Linq;
using System.Data.Entity;
using System.Data.Common;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.ComponentModel;
using DevExpress.ExpressApp.EF.Updating;
using DevExpress.Persistent.BaseImpl.EF;
using DevExpress.Persistent.BaseImpl.EF.PermissionPolicy;
namespace XafApplication1.Module.BusinessObjects {
public class XafApplication1DbContext : DbContext {
public XafApplication1DbContext(String connectionString)
: base(connectionString) {
}
public XafApplication1DbContext(DbConnection connection)
: base(connection, false) {
}
public XafApplication1DbContext()
: base("name=ConnectionString") {
}
public DbSet<ModuleInfo> ModulesInfo { get; set; }
public DbSet<PermissionPolicyRole> Roles { get; set; }
public DbSet<PermissionPolicyTypePermissionObject> TypePermissionObjects { get; set; }
public DbSet<PermissionPolicyUser> Users { get; set; }
public DbSet<ModelDifference> ModelDifferences { get; set; }
public DbSet<ModelDifferenceAspect> ModelDifferenceAspects { get; set; }
}
}
OK, I will take a stab :) Your Up() code is trying to rename the table UserRoles to RoleUsers. This means you have a prior migration where UserRoles was the table name - probably from your DevEx stuff. This could happen if they changed their models in an upgrade. The current models are expecting RoleUsers etc. so you need to get there.
So first option is let the migration do the renaming to match the underlying model. I assume this didn't work or causes other issues?
You might be able to 'fool' entity framework into using the old tables with fluent code or annotations, but if it has new columns or relationships that won't work.
What I would do is this:
1) Create a new test project with the same references you had and
copy your context and DbSets. Point the connection string to a
new database.
2) Add a migration and script it out:
update-database -Script.
3) Examine this script a use it to create
the objects needed in your database. Migrate data from the old
tables to new if needed.
4) Remove the old tables
5) In your actual
project add a migration to resync your models:
add-migration SyncDevExUpdate -IgnoreChange, update-database
Now you will have the tables your models expect.

Database-first key annotations unrecognized by entity framework

I am trying to use partial classes to decorate EF's auto-generated entity models for SQL views that I have access to. These views are 1:1 representations of tables, but omit the primary keys and foreign key constraints present in the database. I would like to reproduce these keys/constraints with data annotations, but attempting to use them within EF fails.
Any attempts to use Include or Find within LINQ always fail. The foreign key defined here, for example, does not seem to be recognized:
public class FactTimeEntryMetadata
{
[Key]
public int TimeEntryKey { get; set; }
[ForeignKey("DimEmployee.EmployeeKey")]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(FactTimeEntryMetadata))]
public partial class FactTimeEntry { }
public class DimEmployeeMetadata
{
[Key]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(DimEmployeeMetadata))]
public partial class DimEmployee { }
In this example, the FactTimeEntry contains TimeEntryKey as a primary, and a column for EmployeeKey, referring to the foreign key in DimEmployee. Is my syntax off in some way, or is this simply not possible with Entity Framework database-first?
Edit:
I have also tried to add a virtual reference to the object, like so:
public class FactTimeEntryMetadata
{
[Key]
public int TimeEntryKey { get; set; }
[ForeignKey("DimEmployee")]
public int EmployeeKey { get; set; }
public virtual DimEmployee DimEmployee { get; set; }
}
[MetadataType(typeof(FactTimeEntryMetadata))]
public partial class FactTimeEntry { }
public class DimEmployeeMetadata
{
[Key]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(DimEmployeeMetadata))]
public partial class DimEmployee { }
but was also unsuccessful this way.
Your only three options using ef-database-first:
Put your keys and relationships in your database. Relational Databases were created to be relational. If you don't do that, why use a database?
As Gert Arnold metion in his comment, edit the EDMX and assign your associations.
Edit the T4 templates with custom code to add Keys/relationships to your objects.

Adding a view to database created by Code First

I have created my first ASP.NET MVC 4 application.
I have created my model which creates the Database (Database A). I now need to gather data from another database (Database B) which sits on the same server. I have created a view (2 columns - ID and Name called People) in Database A that shows me the data I require from Database B.
I'd like to add the view to my model and have typed the following
public class People
{
public int ID { get; set; }
public String Name { get; set; }
}
And added the following line to my dbContext
public class opsDBContext : DbContext
{
public DbSet<tbl_Operators> Operator { get; set; } // Existing
public DbSet<tbl_OpsWeekInfo> OperatorWeekInfo { get; set; } // Existing
public DbSet<tbl_OpsDayInfo> OperatorDayInfo { get; set; } // Existing
public DbSet<People> People{ get; set; } // New Line
}
But when i run project i get the following error
The model backing the 'opsDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database
I think i understand why i get the message, i just want to me able to use the SQL View in my project, how can this be done?
Please let me know if you require more info
When a change occurs in classes of context or in your database, when you are using EF code first, you need to run migrations commands.
Take a look in this link :http://msdn.microsoft.com/en-us/data/jj591621.aspx

Establish Foreign Key Connection Using Entity Framework With SQL Queries

I have a couple of classes (for this example anyway) that use code first with the entity framework to connect to the database.
public class Customer
{
[Key]
public long CustomerId { get; set; }
public string CompanyName { get; set; }
...
public virtual List<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public long ContactId { get; set; }
public string Forename { get; set; }
...
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
When I hook these up in my context class directly to the db the foreign key relationships hook up fine and I can access the collection of contacts from within the customer class.
class RemoteServerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Contact> Contacts { get; set; }
...
}
My problem is that these database tables are used by various different systems and are massive. In order to increase efficiency I have overridden the default behaviour to point at a view (and also a stored proc elsewhere) rather than directly at the table.
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
public IEnumerable<Contact> Contacts()
{
return Database.SqlQuery<Contact>("SELECT * FROM vw_ContactsList");
}
I have made sure that in each of the views I have included the foreign key fields: CustomerId and ContactId.
When I do this however the class joins appear to be lost - there's always a null when I drill into either of the objects where it should be pointing to the other one. I have tried to set up what the foreign key field should point to but this doesn't seem to help either.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasRequired(p => p.Customer)
.WithMany()
.HasForeignKey(k => k.CustomerId);
}
Is there a way to establish the connection when overriding the default behaviour?
There is no overriding in this case. If you removed
public DbSet<Customer> Customers { get; set; }
and replaced it with
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
you have completely changed the behavior. The first uses entities and full power of EF. The second is only helper to execute custom SQL. Second without first or without defining entity in OnModelCreating doesn't use Customer as mapped entity at all - it uses it as any normal class (only mapped entities can use features like lazy loading).
Because your Customer is now mapped to view you cannot use your former Customer class used with table. You must define mapping of Customer to a view by cheating EF:
modelBuilder.Entity<Customer>().ToTable("vw_ContactsList"); // EF code fist has no view mapping
Once you have this you can try again using:
public DbSet<Customer> Customers { get; set; }
Unless your view is updatable you will get exception each time you try to add, update or delete any customer in this set. After mapping relation between Customer and Contact mapped to views your navigation properties should hopefully work.
The problem with SqlQuery is the way how it works. It returns detached entities. Detached entities are not connected to the context and they will not lazy load its navigation properties. You must manually attach each Customer instance back to context and to do that you again need DbSet.

Resources