I am using code first approach for my .net core project. We are using multiple schemas in single database.
We are maintaining models in different class library projects like Inventory,Sales,Finance etc ..
Model mapping is like ( note : below they are different name spaces)
[Table(name: "Product", Schema = "Inventory")]
public class Product
{
public int ProductId {get;set;}
public string Name {get;set;}
}
[Table(name: "Order", Schema = "Sales")]
public class Order
{
public int OrderId{get;set;}
public int ProductId {get;set;}
public virtual Product Product {get;set;}
}
With in the schema i am able to add relation by using attribute. Now i want to add relation between these tables.
I have tried some tweaks but not working.
Any help is appreciated.
Update:
DBcontext's also different for each schema and they are placed in respective class library
You have conflicting approaches in your current architecture:
On one hand, you have gone for a micro-services approach where each service is dealing with its own bounded context (e.g. Inventory, Sales, etc.). This seems to be confirmed by your use of different database schemas, which can be viewed as logically different databases that happen to be deployed in a single physical database. This is fine, and allows for future scaling / segregation where you might move the inventory data into its own database, for example.
On the other hand, you are trying to treat the datastore as a monolithic artefact where you can build table relationships across the bounded context boundary established by your micro-service approach.
If you wish to maintain your micro-services approach, which is perfectly reasonable, then you have to accept that you cannot rely on database-enforced referential integrity for table relationships that span entities in different micro-services.
You'd need a layer above that can retrieve data from separate micro-services and put them together into entities (preferably DTO entities, not the EF Data Entities used for code-first) that the consumer is looking for.
This layer would first retrieve Orders from the 'Sales' service, and then enumerate your Orders and retrieve the relevant Products from the 'Inventory' service and then map those into DTO entities that include navigation properties between OrderDTO and ProductDTO.
Your Order data entity should not have a virtual navigation property to "Product" but instead just hold a unique id of the Product that the Order relates to (not enforced as a database relationship). Personally, I would go further and introduce a GUID Unique Identifier to the Product data class that can be used in the Order to uniquely identify the product. That way, if you ever do migrate your Inventory tables to a new database, you don't have to worry about managing the Database Identity Column during that migration as the reference 'outside' of the Inventory service to the Product table would be the GUID Unique Identifier.
You have a typo
public virtal Product Product {get;set;}
should be virtual
Also you need a navigation member in Product
[Table(name: "Product", Schema = "Inventory")]
public class Product
{
public int ProductId {get;set;}
public string Name {get;set;}
public virtual ICollection<Order> Orders { get; set; }
}
[Table(name: "Order", Schema = "Sales")]
public class Order
{
public int OrderId{get;set;}
public int ProductId {get;set;}
public virtual Product Product {get;set;}
}
Related
Let's define Term as a principal entity and Course as a one to many dependant relationship.
public class Term
{
public int Id { get; set; }
public List<Course> Courses { get; set; } = new List<Course>();
}
public class Course
{
public int Id { get; set; }
public DateTime EndDate { get; set; }
}
A common query criteria for our Term entity is to check whether all of the courses are finished or not and by that we deem the Term as finished or not too, and this implicit state can show itself in a lot of places in our bussiness logic, simple queries to populate view models, etc.
Using ORMs like EntityFramework Core this query can popup in a lot of places.
Terms.Where(t=>t.Courses.All(c=>c.EndTime > DateTime.Now))
Terms.Count(t=>t.Courses.All(c=>c.EndTime > DateTime.Now))
Other examples of this that come to mind are a product and its current inventory count, posts that only contain unconfirmed comments, etc.
What can we consider as best practice if we are to capture these implicit states and make them directly accessible in our principal entity without the need to rehydrate our dependant entity from the database too?
Some solutions that come to mind:
Using a computed column to do a subquery and map it to a property on the principal entity e.g. Term.IsFinished
Defining a normal property on our entity and use a scheduling solution to update its value on predetermined timestamps which is not acceptable in a lot of cases due to inconsistency in different intervals, or use domain events and react upon them to update the property on the principal entity
Create a view, with the two tables joined and aggregated per principal entity.
Use the view directly in Entity Framework instead of the base table.
For bonus points:
In SQL Server you can create a clustered index on the view, and it will be automatically maintained for you. Oracle has a similar concept.
In other RDBMSs, you would need to create a separate table, and maintain it yourself with triggers.
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.
I have multiple projects that return the same OData entities through a API endpoint. Now i want to call all of the projects and store them in my calling projects database with entity framework.
To add them to the db the ID gets overwritten but i want to save the id that the entity has in the projects database as well. so i can still access them if need be and to check if the data isn't already in my database. Because of this i need to add another MainProjectID and projectID column to the entity.
I tried making a new class that has a reference to the entity i want to save but this used new id's for the entities. I also tried inheriting the class but this gave me key conflict issues, and generics don't work either in entity framework(i'm not saying they should). So i'm kinda at a loss right now.
I basically want to save the id as a non-key. Is there any way i can do this without writing entirely new classes and parsing them manually ?
Any help would be greatly appreciated.
We have multiple alternatives here:
In a distributed system, best way to cope with these kinds of ID clashes is to make IDs globally unique. If you can modify how IDs are generated, that would be my choice to go. You can use a UUID (or Microsoft implementation GUID) that will produce a universal unique identifier. Or if that seems like an overkill you can devise a simple mechanism that combines ID with projectID. However you should ensure that the method you will use will not produce any collisions (no two different id-projectId pair will map to same value).
This will ensure that same entity is used throughout your application and no overlaps occur if you try to put records from different sources into the same table. You only need to implement a mechanism to record which ID originated from which source. You can use a reference entity at aggregator for this purpose. You also need to disable auto increment nature of the ID column so that your global unique values are used in table.
You can use different entities for producing and aggregating applications. I don't know your application, but that seems like an OK approach to me since the aggregating application has a different idea about the entity. The aggregating application cares for which application produced the entity, that might make putting the source application identifier into the entry justifiable. Your entities will only differ in that and when you receive the OData object from API you'll need copy all other properties and put project identifier yourself.
You can use the previous solution, but you can use a derived class in order to not to repeat your object properties. This is a better design alternative. However with this method you'll have some problems with the primary key (as you've stated you had). Consider this example
public class Base {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Derived : Base {
[Key]
public int projectId {get; set; }
}
If you don't put [Key] to Derived then you'll have only ID as primary key. When you put [Key] to Derived then you'll have only projectId as primary key. You need to define a composite key and you can do this by removing the [Key] annotation from projectId and using the onModelCreating override of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Derived>().HasKey(a => new { a.ID, a.projectId })
.Property(c => c.ID).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
modelBuilder.Entity<Derived>().Property(c => c.projectId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
}
You can mix these alternatives. You can drop the primary key on ID field, and then you can insert a new Entity that will model 1-M relationship between ID's and project ID's.
I have a project where I am taking an existing database and converting the stored procedures into an Entity Framework respository. I am running into quite a few issues setting up the Entity relationships. I am unable to alter the structure of the database so I have to find a solution within code.
Lets say I have a Clients Table and a Notes table. Clients has a one to many relationship with Notes.
In Clients, I have a field called Client_Id(the primary key) , In the Notes table I have a Note_Id and a Account_ID. The Clients.Client_Id maps to Notes.Account_Id.
Now, in tables that have the same key value I can just create a ICollection and Enity will create the relationship just fine. But since the names are different and there is no foreign keys to map how do I get this to work?
Thanks.
You should be able to configure these mappings using either Data Annotations or the Fluent API. Here's a sample of how you might do it using the annotations:
[Table("Clients")]
class Client
{
[Key]
public int Client_Id {get;set;}
public virtual ICollection<Note> Notes {get;set;}
}
[Table("Notes")]
class Note
{
[Key]
public int Note_Id {get;set;}
public int Account_Id {get;set;}
[ForeignKey("Account_Id")]
public virtual Client Client {get;set;}
}
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.