我正在嘗試執行一個查詢,我必須從資料庫中提取資訊,如下所示
找到包含的inventory數字。1assets
但是我在我的assets物件中得到了重復的結果,我不明白為什么。
詢問:
[HttpGet("Search/")]
public async Task<ActionResult<DtoInventory>> SearhInventory()
{
Inventory queryset = await context.Inventories.Include(i => i.Assets).FirstOrDefaultAsync(i => i.inventory_id == 1);
DtoInventory dto = mapper.Map<DtoInventory>(queryset);
return dto;
}
資料庫背景關系
using API.Models;
using Microsoft.EntityFrameworkCore;
namespace API.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<Requirement> Requirements { get; set; }
public DbSet<Inventory> Inventories { get; set; }
public DbSet<Asset> Assets { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region Inventory
// Table name
modelBuilder.Entity<Inventory>().ToTable("Inventories");
// PK
modelBuilder.Entity<Inventory>().HasKey(i => i.inventory_id);
#endregion
#region Asset
// Table Name
modelBuilder.Entity<Asset>().ToTable("Assets");
// PK
modelBuilder.Entity<Asset>().HasKey(i => i.asset_id);
// Code
modelBuilder.Entity<Asset>().Property(a => a.code)
.HasColumnType("int");
// Relationship
modelBuilder.Entity<Asset>()
.HasOne(i => i.Inventory)
.WithMany(a => a.Assets)
.HasForeignKey(a => a.inventory_id); //FK
#endregion
}
}
}
庫存模型
namespace API.Models
{
public class Inventory
{
public int inventory_id { get; set; }
public string name { get; set; }
public string location { get; set; }
public int status { get; set; }
public DateTime? created_date { get; set; }
public List<Asset> Assets { get; set; }
}
}
DtoInventory
namespace API.Dtos
{
public class DtoInventory
{
public int inventory_id { get; set; }
public string name { get; set; }
public string location { get; set; }
public bool status { get; set; }
public DateTime created_date { get; set; }
public List<Asset> Assets { get; set; }
}
}
預期結果:
{
"inventory_id": 1,
"name": "cellphones",
"location": "usa",
"status": true,
"created_date": "0001-01-01T00:00:00",
"assets":
[
{
"asset_id": 1,
"code": 1,
"name": "iphone x",
"inventory_id": 1
},
{
"asset_id": 2,
"code": 2,
"name": "samsung pro",
"inventory_id": 1
},
{
"asset_id": 3,
"code": 3,
"name": "alcatel ",
"inventory_id": 1
}
]
}
得到的結果:
{
"inventory_id": 1,
"name": "cellphones",
"location": "usa",
"status": true,
"created_date": "0001-01-01T00:00:00",
"assets": [
{
"asset_id": 1,
"code": 1,
"name": "iphone x",
"inventory_id": 1,
"inventory": {
"inventory_id": 1,
"name": "cellphones",
"location": "usa",
"status": 1,
"created_date": null,
"assets": [
null,
{
"asset_id": 2,
"code": 2,
"name": "samsung pro",
"inventory_id": 1,
"inventory": null
},
{
"asset_id": 3,
"code": 3,
"name": "alcatel ",
"inventory_id": 1,
"inventory": null
}
]
}
},
{
"asset_id": 2,
"code": 2,
"name": "samsung pro",
"inventory_id": 1,
"inventory": {
"inventory_id": 1,
"name": "cellphones",
"location": "usa",
"status": 1,
"created_date": null,
"assets": [
{
"asset_id": 1,
"code": 1,
"name": "iphone x",
"inventory_id": 1,
"inventory": null
},
null,
{
"asset_id": 3,
"code": 3,
"name": "alcatel ",
"inventory_id": 1,
"inventory": null
}
]
}
},
{
"asset_id": 3,
"code": 3,
"name": "alcatel ",
"inventory_id": 1,
"inventory": {
"inventory_id": 1,
"name": "cellphones",
"location": "usa",
"status": 1,
"created_date": null,
"assets": [
{
"asset_id": 1,
"code": 1,
"name": "iphone x",
"inventory_id": 1,
"inventory": null
},
{
"asset_id": 2,
"code": 2,
"name": "samsung pro",
"inventory_id": 1,
"inventory": null
},
null
]
}
}
]
}
uj5u.com熱心網友回復:
您將需要另一個 Dto DtoAssetfor EntityAsset
namespace API.Dtos
{
public class DtoInventory
{
public int inventory_id { get; set; }
public string name { get; set; }
public string location { get; set; }
public bool status { get; set; }
public DateTime created_date { get; set; }
// List of Dto Assets
public List<DtoAsset> Assets { get; set; }
}
public class DtoAsset
{
public int asset_id { get; set; }
public int code { get; set; }
public string name { get; set; }
public int inventory_id { get; set;}
}
}
uj5u.com熱心網友回復:
所以你有一張桌子Inventories和一張桌子Assets。Inventory 和 Assets 之間存在直接的一對多關系:每個 Inventory 都有零個或多個 Assets,每個 Asset 恰好屬于一個 Inventory,即外鍵所指的 Inventory。
間奏曲:還有改進的空間
您決定將資料庫中的行與您與用戶(= 軟體,而不是操作員)的通信方式分開。因此,您有單獨的課程Inventory和InventoryDto. 這種分離可能是件好事。如果您預計資料庫布局會發生變化,那么您的用戶不必更改。但是,由于 Inventory 和 InventoryDto 之間的差異非常小,我不確定在這種情況下這種分離是否是一種增強。
- 如果 status 真的是一個布林值,為什么不將它作為布林值保存在資料庫中呢?此外,狀態是一個令人困惑的名稱。真實狀態是什么意思?
- In
Inventory.CreatedDate可以為空。InventoryDto.CreatedDate不是。你為什么要做出這樣的改變?如果資料庫中的 CreatedDate 為空,您將遇到麻煩。您希望 InventoryDto 中的值是多少?
此外,您決定偏離物體框架命名約定。當然你可以自由地這樣做,但是這種偏差使你不得不做更多的編程,就像你在OnModelCreating.
如果您遵循約定,您的 Inventory 和 Asses 類將如下所示:
public class Inventory
{
public int Id { get; set; }
public string name { get; set; }
public string location { get; set; }
public int status { get; set; }
public DateTime? created_date { get; set; }
// Every Inventory has zero or more Assets (one-to-many)
public virtual ICollection<Asset> Assets { get; set; }
}
public class Asset
{
public int Id {get; set;}
... // other properties
// Every Asses belongs to exactly one Inventory, using foreign key
public int InventoryId {get; set;}
public virtual Inventory Inventory {get; set;}
}
主要區別在于,我使用ICollection<Asset>, 而不是 List。對你Inventory.Asset[4]有明確的意義嗎?你會使用 Asset 是一個事實List嗎?如果你使用ICollection,用戶不能使用索引,這很好,因為你不能保證什么物件會有什么索引。此外,這更重要:您不會強制物體框架將獲取的資料復制到串列中。如果物體框架決定將資料以另一種格式放置會更有效,為什么要強制它使用它作為串列呢?
所以在一對多和多對多中始終堅持ICollection<...>這個界面具有您需要的所有功能:您可以從庫存中添加和洗掉資產,您可以Count在庫存中,您可以一一列舉它們. 您需要的所有功能。
在物體框架中,表中的列由非虛擬屬性表示。虛擬屬性表示表之間的關系(一對多,多對多,...)
外鍵是表中的列,因此它們是非虛擬的。每個資產恰好屬于一個庫存這一事實是表之間的關系,因此該屬性是虛擬的。
回到你的問題
找到包含資產的庫存編號 1。
如果您遵循約定,查詢將很容易:
int inventoryId = 1;
using (var dbContext = new WhareHouseDbContext(...))
{
Inventory fetchedInventory = dbContext.Inventories
.Where(inventory => inventory.Id == inventoryId)
.Select(inventory => new
{
// select only the properties that you actually plan to use
Name = inventory.Name,
Location = inventory.Location,
...
// The Assets of this Inventory
Assets = inventory.Assets
.Where (asset => ...) // only if you don't want all Assets of this Inventory
.Select(asset => new
{
// again, only the properties that you plan to use
...
// not needed, you already now the value:
// InventoryId = asset.InventoryId,
})
.ToList(),
})
// expect at utmost one Inventory
.FirstOrDefault();
if (fetchedInventory != null)
{
... // process the fetched data
}
}
Entity framework 知道 Inventories 和 Assets 之間的一對多關系,并且會為你做正確的 (Group-)Join。
資料庫管理系統經過優化,可以組合表格和選擇資料。較慢的部分之一是將所選資料從 DBMS 傳輸到本地行程。
除此之外,您不想傳輸無論如何都不會使用的資料,或者您已經知道其值的資料,例如外鍵,還有另一個原因是不獲取完整物件,也不使用 Include。
每個DbContext都有一個ChangeTrackerwhich 用于檢測如果您呼叫必須更新哪些值SaveChanges。每當您獲取一個完整的物件(表中的行)時,該物件都會被放入 ChangeTracker 以及一個副本中。您得到對副本(或原件,沒關系)的參考。如果您對現有的參考進行更改,則副本會更改。
當您呼叫 SaveChanges 時,ChangeTracker 中的原件將按值與副本進行比較。僅更新更改。
如果您在不使用的情況下獲取大量資料Select,則所有這些專案以及它們的副本都會放入 ChangeTracker 中。一旦呼叫 SaveChanges,所有這些獲取的資料都必須與它們的原始資料進行比較,以檢查是否有更改。如果您不將不想更新的專案放入 ChangeTracker 中,這將極大地提升性能。
在物體框架中始終使用
Select,并僅選擇您實際計劃使用的屬性。僅獲取完整的行,僅Include在您計劃更新獲取的資料時使用。
匿名型別與具體型別
在我的解決方案中,我使用了匿名型別,這讓我可以自由地選擇我計劃以我想要的格式使用的屬性(可為空或不可為空的 CreatedDate,狀態為布林值或 int)。
缺點是匿名型別只能在定義它的方法中使用。如果您確實需要在方法之外使用資料,例如在回傳值中使用它,請調整Select:
.Select(inventory => new InventoryDto
{
Id = inventory.Id,
Name = inventory.Name,
...
Assets = inventory.Assets.Select(asset => new AssetDto
{
Id = asset.Id,
...
})
.ToList(),
}
現在你可以在你的方法之外使用這個物件。
自己做組加入
有些人不想使用virtual ICollection<...>,或者他們使用不支持此的物體框架版本。在這種情況下,您必須自己執行 (Group-)Join
var fetchedInventories = dbContext.Inventories
.Where(inventory => ...)
// GroupJoin the Inventories with the Assets:
.GroupJoin(dbContext.Assets,
inventory => inventory.Id, // from every inventory take the primary key
asset => asset.InventoryId, // from every asset take the foreign key
// parameter resultSelector:
// from every inventory, each with its zero or more Assets make one new
(inventory, assetsOfThisInventory) => new
{
Id = inventory.Id,
Name = inventory.Name,
...
Assets = assetsOfThisInventory.Select(asset => new
{
Id = asset.Id,
...
})
.ToList(),
});
當然,如果需要,請使用具體型別而不是匿名型別。
在一對多關系中,
GroupJoin如果您需要獲取“專案,每個專案都有零個或多個子專案”,請使用并從“一側”開始。如果您需要獲取“專案,每個專案都有一個父專案”,我們Join并從“多方面”開始。
因此,使用 GroupJoin 來獲取學校和他們的學生、客戶和他們的訂單、圖書館和他們的書籍,在你的情況下,庫存和他們的資產。
使用 Join 來獲取學生、每個學生與他就讀的學校、訂單以及下訂單的客戶的資料或資產,以及該資產所屬的唯一庫存。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/471247.html
