slim-ef
Version:
An implementation of basic entity framework functionnalities in typescript
266 lines • 10.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnitOfWork = exports.DbContext = void 0;
const repository_1 = require("../repository");
const db_set_1 = require("../repository/db-set");
const specification_interface_1 = require("../specification/specification.interface");
const metadata_1 = require("./metadata");
const model_builder_1 = require("./model-builder");
const options_builder_1 = require("./options-builder");
class DbContext {
constructor(_connection, evaluator) {
this._connection = _connection;
this.evaluator = evaluator;
this._new = [];
this._dirty = [];
this._deleted = [];
this._entitySets = new WeakMap();
this._modelBuilder = new model_builder_1.DbContextModelBuilder();
this._optionsBuilder = new options_builder_1.DbContextOptionsBuilder();
this._initialise();
}
_initialise() {
this._setUnderlyingEntityType();
this.onModelCreation(this._modelBuilder);
this.onConfiguring(this._optionsBuilder);
}
set(type) {
const dbSet = new db_set_1.DbSet(this);
dbSet[db_set_1.UnderlyingType] = type;
return dbSet;
}
add(...entities) {
this._new.push(...this._throwIfNullFound(entities, 'add'));
}
unTrack(entity) {
this._new = [...this._new.filter(n => n !== entity)];
this._dirty = [...this._dirty.filter(n => n !== entity)];
this._deleted = [...this._deleted.filter(n => n !== entity)];
}
update(...entities) {
this._dirty.push(...this._throwIfNullFound(entities, 'attach'));
}
remove(...entities) {
this._deleted.push(...this._throwIfNullFound(entities, 'remove'));
}
_throwIfNullFound(entities, method) {
if (entities.some(e => !e)) {
throw new Error('Entities can not be null when call ' + method);
}
return entities;
}
async find(type, id) {
await this._tryOpenConnection();
const repo = this._getRepository(type);
if (repo) {
const res = (await repo.findOne(id));
return res;
}
return void 0;
}
rollback(entityType = null) {
if (entityType !== null) {
const type = typeof entityType;
this._new = [...this._new.filter(n => typeof n !== type)];
this._dirty = [...this._dirty.filter(n => typeof n === type)];
this._deleted = [...this._deleted.filter(n => typeof n === type)];
}
else {
this._dispose();
}
}
async saveChanges(withoutRefresh = false) {
await this._tryOpenConnection();
const transIsOpened = this.transactionIsOpen();
if (!transIsOpened) {
this._queryRunner = this._connection.createQueryRunner();
await this._queryRunner.startTransaction();
}
try {
const added = await this._commitNew();
const toUpdate = await this._commitDirty();
const deleted = await this._commitDeleted();
if (!transIsOpened)
await this._queryRunner.commitTransaction();
const updated = withoutRefresh
? toUpdate
: await this._getUpdates(toUpdate);
return { added, updated, deleted };
}
catch (e) {
if (transIsOpened)
await this._queryRunner.rollbackTransaction();
throw e;
}
finally {
if (!this._queryRunner.isReleased && !transIsOpened)
this._queryRunner.release();
this._dispose();
}
}
transactionIsOpen() {
var _a;
return !!((_a = this._queryRunner) === null || _a === void 0 ? void 0 : _a.isTransactionActive) && this._isUserTransaction;
}
async openTransaction() {
if (this.transactionIsOpen()) {
throw new Error('A transaction is already open, please release the later before opening a new one');
}
await this._tryOpenConnection();
this._isUserTransaction = true;
this._queryRunner = this._connection.createQueryRunner();
await this._queryRunner.startTransaction();
}
async commitTransaction() {
if (this.transactionIsOpen()) {
await this._tryOpenConnection();
await this._queryRunner.commitTransaction();
await this._queryRunner.release();
this._queryRunner = null;
this._isUserTransaction = false;
}
}
async rollbackTransaction() {
if (this.transactionIsOpen()) {
await this._queryRunner.rollbackTransaction();
await this._queryRunner.release();
this._queryRunner = null;
this._isUserTransaction = false;
}
}
async query(query, parameters) {
await this._tryOpenConnection();
const result = await this._connection.query(query, parameters);
// await this._tryCloseConenction();
return result;
}
loadRelatedData(type, entity) {
const manager = this._connection.manager;
return manager.preload(type, entity);
}
//#region IInternalDbContext Implementation
async execute(queryable, type = specification_interface_1.QueryType.ALL, ignoreFilters = false) {
await this._tryOpenConnection();
if (!ignoreFilters) {
// applying filters
const filters = this._modelBuilder.getFilters(queryable[db_set_1.UnderlyingType]);
filters.forEach(f => {
f(queryable);
});
}
const initializer = this._getSQLBuilder(queryable);
const specEval = new this.evaluator(initializer, queryable.asSpecification());
const logger = this._optionsBuilder.createLogger('query');
logger === null || logger === void 0 ? void 0 : logger.log('info', await specEval.getQuery());
logger === null || logger === void 0 ? void 0 : logger.log('info', await specEval.getParams());
const result = await specEval.executeQuery(type);
// CLosing the connection here prevents typeorm lazyloading.
// So commenting this, may be I will get e better solution for
// managing these resources
// await this._tryCloseConenction();
return result;
}
async getMetadata(type, includePaths) {
await this._tryOpenConnection();
const md = metadata_1.getMetaData(this._connection, type, includePaths);
// await this._tryCloseConenction();
return md;
}
//#endregion
dispose() {
this._dispose();
this._tryCloseConenction();
}
//#region Private methods
async _tryOpenConnection() {
if (!this._connection.isConnected)
await this._connection.connect();
}
async _tryCloseConenction() {
if (this._connection.isConnected)
await this._connection.close();
}
_dispose() {
this._new = [];
this._dirty = [];
this._deleted = [];
}
_setUnderlyingEntityType() {
const underType = repository_1.getEntitySetKeys(this);
if (underType) {
for (const t of underType) {
const dbset = new db_set_1.DbSet(this);
const entity = repository_1.getEntitySet(this, t);
if (dbset && entity) {
Object.defineProperty(this, t, {
configurable: false,
enumerable: false,
value: dbset,
writable: false
});
dbset[db_set_1.UnderlyingType] = entity;
}
}
}
}
async _getUpdates(toUpdate) {
const updated = [];
for (const n of toUpdate) {
const repo = this._getRepository(n);
updated.push(await repo.findOne(n.id));
}
return updated;
}
async _commitDeleted() {
const deleted = [];
for (const n of this._deleted) {
const repo = this._getRepository(n);
deleted.push(await repo.delete(n));
}
return deleted;
}
async _commitDirty() {
return await this._commitDirtyAll(this._dirty);
}
async _commitDirtyAll(dirty) {
const updated = [];
for (const n of dirty) {
const repo = this._getRepository(n);
updated.push(await repo.save(n));
}
return updated;
}
async _commitNew() {
const added = [];
for (const n of this._new) {
const repo = this._getRepository(n);
added.push(await repo.save(n));
}
return added;
}
_getSQLBuilder(type) {
const repo = this._getRepository(type);
const initializer = (alias) => repo.createQueryBuilder(alias);
return initializer;
}
_getRepository(type) {
var _a;
if (type instanceof db_set_1.DbSet) {
type = type[db_set_1.UnderlyingType];
}
const schema = repository_1.getEntitySchema(type);
if (this._entitySets.has(type))
return this._entitySets.get(type);
if (!schema)
throw new Error(`Schema [${(_a = type === null || type === void 0 ? void 0 : type.constructor) === null || _a === void 0 ? void 0 : _a.name}] not found. You may not have set EntityRepository decorator correctly`);
const repo = this._connection.getRepository(schema);
this._entitySets.set(type, repo);
return repo;
}
}
exports.DbContext = DbContext;
// tslint:disable-next-line: max-classes-per-file
class UnitOfWork extends DbContext {
}
exports.UnitOfWork = UnitOfWork;
//# sourceMappingURL=db-context.js.map