Linking objects in .net core api - entity-framework

I'm trying to display a child object in my .net core api application. I have the following setup;
public class Accounts
{
[Key]
public int AccountId { get; set; }
public string AccountName { get; set; }
public int AccountStatusId { get; set; }
public List<AccountStatus> AccountStatus { get; } = new List<AccountStatus>();
}
public class AccountStatus
{
public int AccountStatusId { get; set; }
public string AccountStatusName { get; set; }
}
Which is being displayed in my controller link this:
[HttpGet]
public IEnumerable<Accounts> Get()
{
return this._internalContext.Accounts.ToList();
}
It works, but I expected AccountStatus to be shown in the result with the AccountStatusName item to use in my json API. As it stands, it returns this:
{
"accountId": 1,
"accountName": "Toms Mega Mix",
"accountStatusId": 1,
"accountStatus": []
},
Where I expceted it to do something like this:
{
"accountId": 1,
"accountName": "Toms Mega Mix",
"accountStatusId": 1,
"accountStatus": [{"AccountStatusName":"Active"}]
},
Account Status return
{
"accountStatusId": 1,
"accountStatusName": "Customer"
},
{
"accountStatusId": 2,
"accountStatusName": "Supplier"
},
{
"accountStatusId": 3,
"accountStatusName": "Ex Customer"
}
Presumably I have to link them somehow but can't work out how

It is not a general .NET Core question but is an Entity Framework Core
question.
UPDATE
Based on our conversation in the chat, it looks like you just need just one AccountStatus per Account.
So, this should work:
public class Accounts
{
[Key]
public int AccountId { get; set; }
public string AccountName { get; set; }
// Optional ID property for Entity Framework navigation to another table. If you don't define it, it will be implicitly created by Entity Framework
public int AccountStatusId { get; set; }
public AccountStatus AccountStatus { get; set; }
}
Previous answer:
To load data from another table, Entity Framework needs to join tables. It's a bit more "expensive" than just querying one table.
So, you need to tell EF to do it for you. For example, you can explicitly load data from a related table by using .Include().
this._internalContext.Accounts
// In general, this should help:
.Include(a => a.AccountStatus)
.ToList();
In your particular example, you also need to fix the Accounts class if you want one-to-many relationship. When many accounts can have one status.
AccountStatus property should be like that:
public class Accounts
{
[Key]
public int AccountId { get; set; }
public string AccountName { get; set; }
public virtual ICollection<AccountStatus> AccountStatus { get; set; }
}
In general, if you want to have many-to-many relationship for any reason, you need to introduce an intermediate entity (and table) for that.
Let's imagine, we have a Course entity. Then you can have many accounts assigned to one course. And each account can be assigned to many courses. It is a many-to-many relationship.
At the same time you have account status per account.
So, the diagram will look like:
AccountToCourse entity is required for many-to-many relationship between Account and Course entities.
Check out more details on that and more ways of controlling the data load behavior:
Microsoft Docs / Entity Framework Core / Query data / Query related data

Related

EF Core - How to define a one-to-one relationship based on two foreign fields with different names?

I am struggling to write the code to generate a foreign key relationship between a one-to-one relationship of Bill and Pay:
public class Bill
{
public string Identifier { get; set; }
public Guid WebSiteId { get; set; }
}
public class Pay
{
public string Account { get; set; }
public Guid WebSiteId { get; set; }
}
The naming was done poorly - Identifier is Account, but it's probably not worth the refactor at this moment (there's static raw SQL queries in the codebase referencing the names directly).
The way to join these two tables in SQL is like this:
SELECT *
FROM Bills b
JOIN Pay p ON (b.Identifier = p.Account AND b.WebSiteId = p.WebSiteId)
And it will guarantee a one-to-one relationship between Bill and Pay.
How do I get EF core to understand this relationship?
I would like to use .Include for my joins:
context.Bills
.Include(x => x.Pays)
Meaning my models would look something like this
public class Bill
{
public string Identifier { get; set; }
public Guid WebSiteId { get; set; }
public virtual Pay Pay { get; set; }
}
public class Pay
{
public string Account { get; set; }
public Guid WebSiteId { get; set; }
public virtual Bill Bill { get; set; }
}
If you need both properties to uniquely identify a Pay, then I presume that you have a composite primary key on Pay entity.
builder.Entity<Pay>().HasKey(p => new { p.Account, p.WebSiteId });
In which case, you can configure the relationship using fluent API:
builder.Entity<Bill>()
.HasOne<Pay>(b => b.Pay)
.WithOne<Bill>(p => p.Bill)
.HasForeignKey<Bill>(b => new { b.Identifier, b.WebSiteId });

Entity Framework: Mapping many-to-many

During my project in which I make some sort of webshop, I've came across a problem with my .NET backend where I use Entity Framework Code First with Fluent API.
In my frontend, Orders can be made and are passed to my backend where they end up as a Order object (code below). This Order contains a User and a Dictionary where Items and their ordered quantities are stored. My current goal is to store those Orders in my database to retrieve an Order history.
My understanding is that by itself, EF can't map a Dictionary. Being a student and having done mostly frontend, I don't really know how to tackle this.
I've tried converting that Dictionary to a List of ItemWrappers (containing both the Item and the amount) and making 2 tables: Order (OrderId, UserId) and OrderItem (OrderId, ItemId, Amount). This converts the Many-to-Many (Users to Items and Order is derived from the relation attribute) to a One-to-Many (Order to OrderItem).
I understand this from a purely database perspective. I could have managed if I were to write all the queries myself, but given that EF adds some abstraction to that, I am a bit lost. How do you suggest I do this?
The code is simplified to only show the class structure. Id is always generated on add and is used as primary key.
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class Order {
public IList<OrderItemWrapper> ItemsList { get; set; }
//Either one of these 2
public Dictionary<Item, int> Items { get; set; }
public User User { get; set; }
public int Id { get; set; }
}
public class OrderItemWrapper {
public Item Item { get; set; }//Will use ItemId as key
public int Amount { get; set; }
}
Could you please go through my explanation for many-to-many relationship here.
Is the following tree of SchoolContext correct?

Entity Framework Core - recursive parent/child linking

I have an "account" table that includes a string foreign-key ("parent_guid") to its "parent" account (if one exists). I would like to create an entity that knows its parent, as well as all of its children.
Here is my entity:
[Table(name:"accounts")]
public class Account
{
[Key]
public string Guid { get; set; }
public string Name { get; set; }
[Column("guid")]
public string accountGuid { get; set; }
[Column(name: "parent_guid")]
public string parentGuid { get; set; }
[ForeignKey("parentGuid")]
public Account Parent { get; set; }
[InverseProperty("Parent")]
public ICollection<Account> Children { get; set; }
}
Here's my dbContext:
public DbSet<Split> Splits { get; set; }
public DbSet<Account> Accounts { get; set; }
public ReportContext(DbContextOptions<ReportContext> options)
: base(options)
{ }
My query is through the 'splits' context as the source table, but I end up returning Accounts. Maybe there's a better way?
When I query for an Account by Guid, I get a result, but 'Parent' and 'Children' are always null, even though 'parentGuid' contains the correct value, and I have confirmed that there should be child records.
Any idea how to make this work, either through annotations or fluent API?
Yes, EF Core requires explicit inclusion of relational entities.
var accounts = await dbContext.Accounts.Include(account => account.Parent)
.Include(account => account.Children)
.ToListAsync();
##EDIT
As per the edits to the question, this is one way to Eager Load relational entities, but I cannot speak to the efficiency of this query without knowing the relations and indexes.
public IQueryable<Split>
FindAllByAccountGuidsPostedBefore(IEnumerable<string> accounts,
DateTime endDate) {
using (reportContext) {
return reportContext.Splits.Include(s => s.Account)
.ThenInclude(a => a.Parent)
.ThenInclude(a => a.Children)
.Where(s => accounts.Contains(s.Account.Guid)
&& s.Transaction.postDate < endDate);
}
}
One way to obtain that information is by looking at the console when this query is run to find the SQL statement(s) this produces, or by asking someone who is more experienced in Relational Databases :)

How to properly define DbContext derived class when using EF Code First with DDD methodology?

I am new to EF, Code First and DDD and still in the learning process, so let's say I have simple domain like this
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public DateTime OrderDate { get; set; }
public List<LineItem> LineItems { get; set; }
}
public class LineItem
{
public Product Product { get; set; }
public int Quantity { get; set; }
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
After domain is defined the next step is to create DbContext derived class and my question is how should it look like? What is a context class definition driven by? Is it by the use cases of the final application?
For example, looking at the domain above we can see that given the Customer instance we can access any child object. So is it then enough to make the context class contains only Customers property look like this:
class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
With this I can browse customers from my app and then select one to see details and orders history etc... Looks good for now.
Now let's say I want following feature in my application:
- list last 10 orders in store (no matter who was the customer)
- list all orders with specific product in it
I guess that this data can be pulled from the current context right? Eg. last 10 orders:
using (var context = new MyContext())
{
var lastTenOrders = context.Customers.Include("Orders")
.Select(customer => customer.Orders)
.SelectMany(orderList => orderList)
.OrderByDescending(order => order.OrderDate)
.Take(10)
.ToList();
}
And getting all orders that contain product with Id = 5:
using (var context = new MyContext())
{
int productId = 5;
var lastTenOrders = context.Customers.Include("Orders")
.Select(customer => customer.Orders)
.SelectMany(orderList => orderList)
.Where(order => order.LineItems.Where(i => i.Product.Id == productId).Any())
.ToList();
}
(note I didn't test these queries so not sure if they work but they describe my general idea)
So I guess this would work but I am wondering is this the right path. Queries can get pretty complex here and it would probably be easier if I add say Orders and Products to the DbContext:
public class MyContext : DbContext
{
public DbSet Customers { get; set; }
public DbSet Orders { get; set; }
public DbSet Products { get; set; }
}
On the other hand I am not sure if I should add orders since they can already be retrieved from the Customer etc...
To sum it up, what are best practices when defining DbContext (and domain model for that matter) and should that be driven by the features (use cases) of the application? Feel free to use and change code in my example above in your explanation.
Consider protected setters for your properties.
Otherwise you could end up with 'data entities' rather then a proper domain model.
public class Customer
{
public string Address { get; protected set; }
public MoveTo(string newAddress)
{
if (newAddress == null) throw new ArgumentNullException("newAddress");
// and other address sanity checks..
Address = newAddress;
DomainEvent.Publish(new CustomerMoved(CustomerNumber, newAddress));
}
}

Entity Framework TPH Inheritance Data Modeling Issues

I'm new to Entity Framework and C#/.Net and trying to create a TPH inheritance model, I'm not sure if I should be or not, so if not, please advise,
Here's the model:
public abstract class Vote
{
public int VoteID { get; set; }
public int UserID { get; set; }
public bool Value { get; set; }
public DateTime DateCreated { get; set; }
public User User { get; set; }
}
public class ProjectVote_ : Vote
{
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
}
public class CommentVote_ : Vote //There are three more like this, votes for different hings
{
public int CommentID { get; set; }
public virtual Comment Comment { get; set; }
}
Now the Project model (comment and model is similar)
public class Project
{
public int ProjectID { get; set; }
public string Title { get; set; }
public virtual ICollection<Vote> Vote { get; set; }
}
What happens is that ICollection creates a database column Project_ProjectID as the foreign key in the Vote table (I think) instead of using the ProjectID I defined. How do I fix it or should I model it differently. If the fluent API is the way to fix it, I don't know how to do that.
In the end I want to be able to use one table to store 5 different types of votes.
When you have related entities you don't need to have a property to store the FK in your model. Entity framework knows that it needs to make a FK to the Project table in ProjectVote when it detects Project in your ProjectVote_ model. Same thing with User and UserId and Comment and CommentId. You don't need to have a property that stores the FK in your model.
You are getting the FK column with the name you don't like "Project_ProjectID" because Entity framework is detecting that it needs to create a FK for your navigation property "Project". It's using it's own naming convention to create the column hence "Project_ProjectID".
If you want to provide your own name for the column override OnModelCreating in your DBContext class and add this fluent mapping.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasMany(p => p.Vote)
.HasRequired(v => v.Project) //or .WithOptional(v => v.Project)
.Map(m => m.MapKey("ProjectId")); //or any other name you want.
}
And for the future this is a helpful reference for how to use the Fluent API. For example here is some documentation on how to custimize TPH with fluent.
Hope that helps!