How do I access 2 DBContext in one Razor page - entity-framework

In my Razor page I would like to use a second DBContext that is connected to a different database.
I have two DBContext that are connecting to two different database that work fine independently in the same app. DB1 is connected to MS Sql Server running on a Linux box, this is the main database. DB2 is connected to MS Sql Server on a Windows Server 2016. I can create CRUD for tables in DB1 and all functions work correctly, I can create Read for a View in DB2 and data is retrieved as expected.
When creating a new record in DB1 I would like to merge data from DB2. How do I create/access a DBContext for DB2 in a Razor page CRUD created for a DBContext for DB1.
I hope this makes sense. I have tried for the last couple of days googling like crazy and haven't been able to find a solution.

BattlFrog - thank you for you response. It wasn't exactly what I was looking for but it did put me on a better Gooogle Path. How I solved my issue was by the use of "Dependency Injection". I simply added the second DBContext to the contructor of the PageModel. Then in my OnPostAsync() I just had to reference the DBContext.
This worked for me, but as I am only learning C#, ASP.Net Core and Razor Pages, this may not be the best approach. Please correct me if I am wrong.
public class IndexModel : PageModel
{
DBContext1 _context1;
DBContext2 _context2;
public IndexModel(DBContext1 context1, DBContext2 context2)
{
_context1 = context1;
_context2 = context2;
}
public async Task<IActionResult> OnPostAsync()
{
IList<ClassName> listName = await _context2.ObjectName.ToListAsync();
// do some stuff here
await _context1.SaveChangesAsync();
}
}

It is best practice to not directly interact with your DbContext in the view, you would make your calls in the Controller then populate a ViewModel with the values from each db, then pass the viewmodel between the view and controller.
So lets say DB1 has our Student data and DB2 has out Teacher data. We need models:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Teacher
{
public string Id { get; set; }
public string Name { get; set; }
}
public class StudentViewModel
{
public Student Student { get; set; }
public Teacher Teacher { get; set; }
}
In our controller we get the data from each DB then combine in the ViewModel:
var studentData = context1.ModelA.GetStudent(studentId);
var teacherData = context2.ModelA.GetTeacher(teacherId);
var viewModel = new StudentViewModel()
{
Student = studentData,
Teacher = teacherData
};
return View(viewModel);
In the return of the controller we are returning the viewModel to the view. Then in the view you would do what ever it is you are doing, then post the viewmodel back to the controller to save in the separate Dbs.
View:
#model MyApp.Models.StudentViewModel
<!-- Do stuff -->

Related

Can't create related entity in ASP.NET Core with EF Core

I have a problem creating a related entity in Entity Framework Core 2.0. I've just created the solution, consisting of an Asp.Net Core backend project, and a UWP project to act as client. Both solutions share model. The two models are:
public class UnitOfWork {
public int UnitOfWorkId { get; set; }
public string Name { get; set; }
public Human Human { get; set; }
}
public class Human {
public int HumanId { get; set; }
public string Name { get; set; }
public List<UnitOfWork> WorkDone { get; set; }
}
As you can see, model is very simple. One human has many units of work. By the way, the backend is connected to an Azure SQL database. I've seen the migration classes, and the database schema looks good to me.
The problem I have is when I want to create a unit of work referencing an existing human, using HTTP. The controller is fairly simple:
[HttpPost]
public UnitOfWork Post([FromBody] UnitOfWork unitOfWork) {
using (var db = new DatabaseContext()) {
db.UnitsOfWork.Add(unitOfWork);
var count = db.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
}
return unitOfWork;
}
Again, nothing fancy here.
How can I create an unit of work, and assign it to an existing human? If I try it with an existing human, in this way
var humans = await Api.GetHumans();
var firstHuman = humans.First();
var unitOfWorkToCreate = new UnitOfWork() {
Name = TbInput.Text,
Human = firstHuman,
};
I get this error:
Cannot insert explicit value for identity column in table 'Humans' when IDENTITY_INSERT is set to OFF
I feel that setting IDENTITY_INSERT to ON will solve my problem, but this is not what I want to do. In the client, I'll select an existing human, write down a name for the unit of work, and create the latter. Is this the correct way to proceed?
EDIT: Following #Ivan Stoev answer, I've updated the UnitOfWork controller to attach unitofwork.Human. This led to
Newtonsoft.Json.JsonSerializationException: 'Unexpected end when deserializing array. Path 'human.workDone', line 1, position 86.'
Investigating - seen here - EFCore expects to create collections (like human.WorkDone) in the constructor, so I did it, and no more nulls deserializing. However, now I have a self-referencing loop:
Newtonsoft.Json.JsonSerializationException: Self referencing loop detected with type 'PlainWorkTracker.Models.UnitOfWork'. Path 'human.workDone'.
Any ideas? Thanks!
The operation in question is falling into Saving Disconnected Entities category.
Add methods marks all entities in the graph which are not currently tracked as new (Added) and then SaveChanges will try to insert them in the database.
You need a way to tell EF that unitOfWork.Human is an existing entity. The simplest way to achieve that is to Attach it (which will mark it as Unchanged, i.e. existing) to the context before calling Add:
db.Attach(unitOfWork.Human);
db.Add(unitOfWork);
// ...

Can't Get EF 6 Code First To Create the Tables

I already have a database with tables outside EF scope. But I want that the tables which will be used by EF to be created automatically.
public class SessionInfo
{
public Guid Id {get;set;}
public string Name { get; set; }
public DateTime StartsOn { get; set; }
public DateTime EndsOn { get; set; }
public string Notes { get; set; }
}
public class StudentsDbContext:DbContext
{
public StudentsDbContext():base("name=memory")
{
Database.Log = s => this.LogDebug(s);
}
public DbSet<SessionInfo> Sessions { get; set; }
}
This code just throws an exception because the table SessionInfoes doesn't exist.
using (var db = new StudentsDbContext())
{
db.Sessions.Add(new SessionInfo() {Id = Guid.NewGuid(), Name = "bla"});
var st = db.Sessions.FirstOrDefault();
}
What do I need to do so that EF will create the "SessionInfoes" (whatever name, it's not important) table by itself? I was under the impression that Ef will create the tables when the context is first used for a change or a query.
Update
After some digging, it seems that EF and Sqlite don't play very nice together i.e at most you can use EF to do queries but that's it. No table creation, no adding entities.
EF needs additional information in order to do this. You'll have to specify an IDatabaseInitializer first. Take a look at this list and find one that is appropriate for your needs (for example: MigrateDatabaseToLatestVersion, DropCreateDatabaseAlways, DropCreateDatabaseIfModelChanges, etc).
Then create your class:
public class MyDatabaseInitializer : MigrateDatabaseToLatestVersion
<MyDbContext,
MyDatabaseMigrationConfiguration>
Then also create the configuration for the initializer (ugh right?):
public class DatabaseMigrationsConfiguration
: DbMigrationsConfiguration<MyDbContext>
{
public DatabaseMigrationsConfiguration()
{
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = true;
}
protected override void Seed(MyDbContext context)
{
// Need data automagically added/update to the DB
// during initialization?
base.Seed(context);
}
}
Then one way to initialize the database is:
var myContext = new MyDbContext(/*connectionString*/);
Database.SetInitializer<MyDbContext>(new MyDatabaseInitializer());
myContext.Database.Initialize(true);
Some people prefer the to use the command line to migrate databases, but I don't want to assume I'll always have access to the database from a command lin.

WCF with Entity Framework Code First

I want to use EF behind my WCF service to fetch data and display it to the client. I need the following suggestions:
Do I need to have the same interface for all the views (e.g. students, teachers etc.) or do I need to have a different interface and service for every table (or view)
Do I need to generate the database calls within my service (.svc) or some other architecture is preferred?
public Student[] GetAllStudents()
{
//database generation code here
}
How can I use EF code-first approach to generate database. I know that for an MVC app, you need to set the initializer in Global.asax or in web.config but I am not sure how it's called in this case. My model looks like this:
[DataContract]
public class Student
{
[DataMember]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember]
public string Subject { get; set; }
[DataMember]
public string Description { get; set; }
}
What you really should do is break up your system in to more separate layers. Instead of having a WCF call that directly queries the database, create a "buisness logic" layer that translates the information that the WCF call provides you to what the EF call needs to know. This is called a N-Tier application
public class SchoolAPI : ISchoolAPI
{
private DataAccessLayer _dal = new DataAccessLayer();
public Student[] GetAllStudents()
{
return _dal.GetStudents(null, null);
}
public Student[] GetAllScienceStudents()
{
return _dal.GetStudents(null, DataAccessLayer.ScienceStudentType);
}
}
private class DataAccessLayer
{
public static readonly ScienceStudentType = //...
public Student[] GetStudents(string subject, string type)
{
using(var ctx = new SchoolContext())
{
IQueryable<Student> studentQuery = ctx.Students;
if(subject != null)
studentQuery = studentQuery.Where(s=>s.Subject == subject);
if(type != null)
studentQuery = studentQuery.Where(s=>s.Type == type);
return studentQuery.ToArray();
}
}
}
The caller of the WCF call does not need to know what the string ScienceStudentType is, all it cares about is that it gets the science students. By seperating the business logic from the database call the caller of your service no longer needs to know.
For EF it will initialize on the first time the framework goes out to "touch" the database and detects that it is not there if it is set up to do so. This is done in the constructor of SchoolContext but is getting a little too broad for this answer. I recommend finding a tutorial on EF and get it working in a simple test enviorment without WCF (maybe a simple console app that just calls GetStudents() then move in in to a WCF environment.

ASP.net MVC.2 return multiple objects to strongly typed viewmodel

I am new to MVC and using the repository pattern in an attempt to select data containing two objects to return to a strongly typed viewmodel,
Im getting a bit stuck with what the best way to do this is,
The two tables are related by a customer id field, i have a repository interface set up, and a display template that is strongly typed to a viewmodel that contains properties for the Customer and a Customer Site object, all i need is to display a list of customer sites along with the relevant customer name from the customers table.
In the display template i have the following
<%= Html.DisplayFor(x => x.Customers.CustomerName) %>
<%= Html.DisplayFor(x => x.Customers.Site.AddressLine1) %>
I have this display template working but my model is empty.
Where im getting confused is how to define this data in the interface and repository, and how to return the data to my model, to simply return my list of customers i use this in my interface
IQueryable<Customer> Customers { get; }
Then a simple LINQ select.
But as this data will contain both customers and customer sites im unsure how to define this in the interface?
Also will a LINQ join be a suitable method to return this data to my viewmodel? something like
var Customers =
from c in customers
join cs in customerSites on c equals cs.CustomerId into ps
from p in ps
select new { Customer = c, cs.CustomerName };
UPDATE=========
This is the code i am using in the view model that is stronly typed to the display template,
public class CustomerViewModel
{
public int Id { get; set; }
public string CustomerName { get; set; }
public string PrimaryContactName { get; set; }
public SiteViewModel Site { get; set; }
}
Its how to populate the model in the repository/controller with both objects to display in a list that im struggling with.
You may have done soem of the following steps already so please ignore if you have..
1 creat a ViewModel folder in your solution.
2 Create a base view model .... might look like this ->
public class BaseViewModel
{
public PageProperties PageProperties { get; set; }
public User User { get; set; }
}
3 Setup a view model for your controller action maybe like so ->
public class ProjectVM : BaseViewModel
{
public ProjectPoco project { get; set; }
}
4 In your controller get your data from your repositiory and pass it to an instance of your view model like this ->
var contextVM = new ProjectVM();
contextVM.project = ObjectExtensions.Convert<ProjectPoco>(tbl.Single(id));
contextVM.PageProperties = new PageProperties
{
Heading = contextVM.project.Name,
SubHeading = "Details for" +
contextVM.project.Name
};
return View(contextVM);
5 set your views model to be that of your view model ->
#model
NerveCentre.ViewModels.ProjectVM
6 use your viewmodel to pull data out into your view ->
#Model.project.Description
A quick and rough description of passing data to your view via a view model. hope I didnt miss anything out.
As for the data.. looking at how you have the model (Customers.Site.AddressLine1) would it not just be possible to pass the Customers from your query to your view model?
So your viewmodel might look something like..
public class SomeViewModel: BaseViewModel
{
public List<Customer> Customers { get; set; }
}
If you let us know what you are using for data access then we might be able to help more with the specifics of getting the data out of your tables and into the format you want?

Silverlight 4 wcf ria Saving multiple records

Ok, I'm pretty sure its just a matter of learning... but I have a very normalized db i'm working with so when I save to my product tbl I also have a productDollar tble and so on...
my question is in silverlight everything is async so How do I save a product get back its new id and use that as the productDollar.productID fk
so far with my other saves i just use the submitOperation in the callback of the submitchanges
and in there i check for iscompleted and do the next save and so on... and chain them together like that.
but I have 500 products I need to save (all at once)
so doing a foreach around my product object won't work because of the wonderful async
So what am I missing??? any help or pointers would be GREATLY appreciated
WCF RIA Services had this situation in mind when it was created. You can easily do it all in one SubmitChanges request and in one database transaction (depending on your DB and/or ORM). However, if you provide some more information about your objects (POCO, EF, etc.), you'll get a better answer.
That said, I'll take a wild guess at your objects as defined on the server.
public class Product
{
[Key]
public int? ProductID { get; set; }
// ... more properties ...
[Association("Product-ProductDollars", "ProductID", "ProductID", IsForeignKey = false)]
[Include]
[Composition]
public ICollection<ProductDollar> ProductDollars { get; set; }
}
public class ProductDollar
{
[Key]
public int? ProductDollarID { get; set; }
public int? ProductID { get; set; }
// ... more properties ...
[Association("Product-ProductDollars", "ProductID", "ProductID", IsForeignKey = true)]
[Include]
public Product Product { get; set; }
}
And your DomainService looks something like
public class ProductDomainService : DomainService
{
public IQueryable<Product> GetProducts()
{
// Get data from the DB
}
public void InsertProduct(Product product)
{
// Insert the Product into the database
// Depending on how your objects get in the DB, the ProductID will be set
// and later returned to the client
}
public void InsertProductDollar(ProductDollar productDollar)
{
// Insert the ProductDollar in the DB
}
// Leaving out the Update and Delete methods
}
Now, on your client, you'll have code that creates and adds these entities.
var context = new ProductDomainContext();
var product = new Product();
context.Products.Add(product);
product.ProductDollars.Add(new ProductDollar());
product.ProductDollars.Add(new ProductDollar());
context.SubmitChanges();
This results in one request sent to the DomainService. However, WCF RIA splits this ChangeSet containing the 3 inserts into 3 calls to your DomainService methods:
InsertProduct(Product product)
InsertProductDollar(ProductDollar productDollar)
InsertProductDollar(ProductDollar productDollar)
If your DomainService performs all inserts in one transaction, the ProductID can be correctly managed by your ORM.