node.js - Modeller refererar till varandra fel: problem med cirkulära beroenden
admin.model.ts
import mongoose, { Schema, Document } from 'mongoose';
import UserRole, { IUserRole } from './user-role.model';
export interface IAdmin extends Document {
role: IUserRole;
}
let adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: UserRole
}
});
export default mongoose.model<IAdmin>('Admin', adminSchema);
användarroll.model.ts
import { Schema, Document, model } from 'mongoose';
export interface IUserRole extends Document{
updated_by: IAdmin|string;
}
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: Admin
}
})
export default model<IUserRole>('UserRole', userRoleSchema);
MongooseError: Invalid ref at path "updated_by". Got undefined
at validateRef (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/helpers/populate/validateRef.js:17:9)
at Schema.path (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:655:5)
at Schema.add (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:535:14)
at require (internal/modules/cjs/helpers.js:88:18)
[ERROR] 22:25:54 MongooseError: Invalid ref at path "updated_by". Got undefined
Här är mina två modeller, hur kan jag lösa denna typ av problem och hur hanterar man cirkulära beroenden?
Svar #1
Du måste sätta ref-värdena för UserRole och Admin i '' på detta sätt:
const adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: 'UserRole' // Name of the model you are referencing
}
});
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: 'Admin' // <- and here
}
})
Svar #2
Medan svaret ovan av @gabriel-lupu är korrekt, verkar det i nyare versioner rekommenderas en annan lösning.
Fixa:
- lägga till citattecken:
ref: 'UserRole'// Rekommenderad första åtgärd - lägg till en pilfunktion:
ref:() => UserRole
Typegoose-dokumentationen säger följande:
Alternativen ref och type kan båda också definieras utan() =>, men det rekommenderas generellt att använda det med. Om() => inte används kan det uppstå problem när klassen (/ variabeln) definieras efter dekoratorn som kräver den. Avsnitt i dokumentationen för referens.
På grund av att ordningen på klasser laddas och ordnas om vid körning, kan detta resultera i att vissa referenser är null/undefined/inte existerar. Detta är anledningen till att Typegoose tillhandahåller följande:
class Nested {
@prop()
public someNestedProperty: string;
}
// Recommended first fix:
class Main {
@prop({ ref: () => Nested }) // since 7.1 arrow functions can be used to
defer getting the type
public nested: Ref<Nested>;
}
// Not recommended workaround (hardcoding model name):
class Main {
@prop({ ref: 'Nested' }) // since 7.0 it is recommended to use "console.log(getName(Class))" to get the generated name once and hardcode it like shown here
public nested: Ref<Nested>;
}Avsnitt av dokumentationen med exemplet ovan
Förklaring:
Felet MongooseError: Ogiltig referens vid sökvägen "updated_by". Fick undefined verkar bero på ett cirkulärt beroende.
IAdminhar enrole-egenskap som refererar tillIUserRole.IUserRolehar enupdated_by-egenskap som refererar tillIAdmin.
- När
admin.model.tsladdas importerar detuser-role.model.ts user-role.model.tsbehöverIAdminförupdated_by-egenskapen frånadmin.model.tsadmin.model.tshar inte laddats färdigt än vilket resulterar i attupdated_bysom refererar till det blir undefined.
Relevant förklaring från Mozilla om hur moduler hanterar cirkulära beroenden.
I ett cykliskt beroende hamnar du med en slinga i grafen. Vanligtvis är detta en lång slinga. Men för att förklara problemet kommer jag att använda ett påhittat exempel med en kort slinga.
Ett komplext moduldiagram med en cykel av 4 moduler till vänster. En enkel cykel av 2 moduler till höger.
Låt oss se hur detta skulle fungera med CommonJS-moduler. Först skulle huvudmodulen köra tills require-satsen. Sedan skulle den gå vidare för att ladda counter-modulen.
Ett commonJS-modul, med en variabel som exporteras från main.js efter ett require-uttalande till counter.js, som beror på den importen
Räknemodulen skulle då försöka få åtkomst till meddelandet från exportobjektet. Men eftersom detta ännu inte har utvärderats i huvudmodulen kommer detta att returnera undefined. JS-motorn kommer att tilldela utrymme i minnet för den lokala variabeln och sätta värdet till undefined.
Meddelandevariabeln kommer att initialiseras och läggas till i minnet. Men eftersom det inte finns någon koppling mellan de två, kommer den att förbli odefinierad i den nödvändiga modulen.
Om exporten hanterades med live-bindningar skulle räknarmodulen så småningom se det korrekta värdet. När timeouten körs skulle main.js:s utvärdering ha slutförts och fyllt i värdet. hur moduler importeras
Båda? lösningarna verkar skapa en direktbinding.
Closures kan också stänga över importerade värden, vilka betraktas som levande bindningar, eftersom när det ursprungliga värdet ändras, ändras det importerade värdet därefter.
// closureCreator.js
import { x } from "./myModule.js";
export const getX = () => x; // Close over an imported live binding


