Skip to content
This repository has been archived by the owner on Feb 13, 2018. It is now read-only.

BASE.data.DataContext

Jared Barnes edited this page Dec 9, 2015 · 24 revisions

The DataContext helps developers handle changes to models. Think of the DataContext as a transaction handler. It meets users' need to query and modify data/relationships. The DataContext handles relationships so users don't need to update foreign or primary keys from a service. The DataContext allows users to think about data as it should be, just data, not so much about the storage structure for persisting the data.

The DataContext uses several other technologies to handle complexity. It uses Queryables for querying the Service, Futures for asynchrony while interacting with the Service, and Observables for checking whether properties have changed on a instance of a model.

Here is a list of the other technologies the DataConext uses:

The DataContext is perfect to use behind a form. Forms usually consist of related data on one or more models. The changes that happen on the form should be handled with the DataContext on a save button click. Here is how it might look:

//...
var edm = new Edm();
var service = new Service(edm);
//...

saveButton.addEventListener("click", function(){
    var firstName = firstNameInput.value;
    var lastName = lastNameInput.value;
    var id = idInput.value;

    var dataContext = new BASE.data.DataContext(service);

    dataContext.people.firstOrDefault(function(expBuilder){
        return expBuilder.property("id").isEqualTo(id);
    }).then(function(person){
        person.firstName = firstName;
        person.lastName = lastName;

        dataContext.saveChangesAsync().then(function(response){
            console.log("Saved Data!");
        });
    });

});

//...

Notice the life of the DataContext is contained within the click function. A service, alternatively, is a long lived object that keeps no memory of the objects being queried for. The service is simply the portal to the persistent data layer. The DataContext uses the service to maintain the data in a transaction.

The DataConext requires a service and a service requires an [edm] (https://github.com/SourceDecoded/BASE/wiki/BASE.data.Edm) (Entity Data Model) . The edm describes the model as it appears in storage, and how the data is associated.

#DataSet

DataSets are added automatically from the definitions of the model in the edm. The edm relies on the "collectionName" property found on the model description.

//...
var edm = new BASE.data.Edm();

var personModel = {
    type: Person,
    collectionName: "people",
    properties: {
        id: {
            type: Integer,
            primaryKey: true,
            autoIncrement: true
        },
        firstName: {
            type: String
        },
        lastName: {
            type: String
        }
    } 
};

edm.addModel(personModel);

var service = new BASE.data.services.InMemoryService(edm);

//...

var dataContext = new BASE.data.DataContext(service);

console.log(dataContext.people); // ==> DataSet

//...

DataSets are queryables. This means users can start a query like so:

//...
dataContext.people.where(function(expBuilder){
    return expBuilder.property("firstName").isEqualTo("John");
}).toArray();

#DataSet Instance Methods

dataSet.add(entity)

Adds an entity to the dataContext. This is like INSERT.

../

var person = new Person();
dataContext.people.add(person);

dataContext.saveChangesAsync().try();

../

dataSet.remove(entity)

Removes an entity to the dataContext. This is like DELETE.

../

dataContext.people.remove(person);

dataContext.saveChangesAsync().try();

../

dataSet.attach(entity)

Attaches an entity to the dataContext without causing any change tracker changes. It may throw an error if the entity or any of the associated entities connected to this entity are already loaded into the dataContext. To avoid errors use dataSet.load.

../

var person = new Person();
person.id = 3;

dataContext.people.attach(person);

../

dataSet.load(entity)

Used to load data transfer objects (dto's). Unlike attach, it will clone the object passed and return the newly loaded entity or already loaded entity.

../

var person = new Person();
person.id = 3;

var loadedPerson = dataContext.people.load(person);

../

dataSet.createInstance(entity)

Creates an instance of the dataSet Type and loads it into the DataContext.

../

var person = dataContext.people.createInstance();
person.firstName = "John";
person.lastName = "Doe";

dataContext.saveChangesAsync().try();

../

DataContext instance methods

dataContext.addEntity

Adds entities to the dataContext. This method will place the entity into the added bucket, and upon saving will put the entity into the loaded bucket.

dataContext.addEntity(entity) ==> undefined

var person = new Person();

dataContext.addEntity(person:any);

dataContext.loadEntity

Loads entities to the dataContext. It will clone the entity that was supplied and return the entity that was loaded. This is useful if you need to load an entity, but don't know if it is already loaded.

dataContext.loadEntity(Type: Function, entity:any) ==> loadedEntity

var dto = {
    id: 1,
    firstName: "John",
    lastName: "Doe"
};

var loadedEntity = dataContext.loadEntity(Person, dto);

dataContext.attachEntity

Attaches entities to the dataContext. It will not clone any entities, but it will throw errors if any of the entities attached are already loaded into the DataContext instance.

dataContext.attachEntity(entity:any) ==> undefined

var person = new Person();
person.id = 1;
person.firstName = "John";
person.lastName = "Doe";

dataContext.attachEntity(person);

dataContext.removeEntity

Removes entities from the dataContext. It will place the entity into the remove bucket, and upon saving will put the entity into the loaded bucket.

dataContext.removeEntity(entity:any) ==> undefined

dataContext.people.firstOrDefault(function(expBuilder){
    return expBuilder.property("firstName").isEqualTo("John");
}).then(function(person){
    dataContext.removeEntity(person);
    dataContext.saveChangesAsync().try();
});

dataContext.detachEntity

This will forget the entity was ever loaded into the data context.

dataContext.detachEntity(entity:any) ==> undefined

dataContext.people.firstOrDefault(function(expBuilder){
    return expBuilder.property("firstName").isEqualTo("John");
}).then(function(person){
    dataContext.detachEntity(person);
    // The entity is now not part of this data context.

    var dataContext2 = new BASE.data.DataContext(service);
    dataContext.attachEntity(person);
});

dataContext.syncEntity

Updates an entity's data with a newer version without causing change tracker changes. This is useful in a web socket environment.

dataContext.syncEntity(entity:any, dto: any) ==> undefined

dataContext.people.firstOrDefault(function(expBuilder){
    return expBuilder.property("firstName").isEqualTo("John");
}).then(function(person){
    var dto = {firstName:"Jane"};
    dataContext.syncEntity(person, dto);
    console.log(dataContext.getPendingEntities().updated.length);
});

dataContext.saveEntity

Saves the entity and the entities that it depends on. This returns a future.

dataContext.saveEntity(entity:any) ==> Future<any>

var person = dataContext.people.createInstance();

person.firstName = "John";
person.lastName = "Doe";

dataContext.saveEntity(person).then(function(){
    console.log("Saved");
}).ifError(function(error){
    console.log("Error");
});

dataContext.saveChangesAsync

Saves all changes in the change tracker buckets.

var person = dataContext.people.createInstance();
person.firstName = "John";

dataContext.saveChangesAsync().try();

dataContext.saveChangesSequentially

Saves all the entities sequentially. There are times when the order in which you save them matters. The data context remembers which order things where added, updated and deleted.

var person = dataContext.people.createInstance();
var address = dataContext.address.createInstance();

person.firstName = "John";
person.address = address;

dataContext.saveChangesSequentially().try();

dataContext.asQueryableLocal

Queries all the loaded entities of that type.

dataContext.people.asQueryableLocal().toArray().then(function(people){
    people.forEach(function(person){
        // Do something special
    });
});

dataContext.asQueryable

Queries all the entities in the persistent database.

dataContext.people.asQueryable().toArray().then(function(people){
    people.forEach(function(person){
        // Do something special
    });
});

dataContext.getQueryProvider

Gets the query provider for the specified Type.

var provider = dataContext.getQueryProvider(Person)
var queryable = new BASE.query.Queryable();

queryable.provider = provider;

queryable.where(function(expBuilder){
    return expBuilder.property("firstName").isEqualTo("Jared");
}).toArray().then(function(people){
    //Do something.
});

dataContext.getOrm

Returns the orm used to maintain relationships.

var orm = dataContext.getOrm();

dataContext.getService

Returns the service used by the data context.

var service = dataContext.getService();

dataContext.getLoadedEntities

Return a BASE.collections.MultiKeyMap<Type, Hashmap> of loaded entities.

var loadedEntities = dataContext.getLoadedEntities();

var hashmapPeople = loadedEntities.get(Person);
var loadedPeople = hashmap.getValues();

dataContext.getPendingEntities

Returns the meta data concerning which entities needed to be added, updated and removed.

var buckets = dataContext.getPendingEntities();

var addedEntitiesMetaData = buckets.added;

var metaData = addedEntitiesMetaData [0];
var entity = metaData.entity;
var timeChanged = metaData.timestamp;

dataContext.dispose

Terminates the data context, but holds on to entities that were part of the data context and hands them off to other data context instances. **note: I removed allows, but we can add it back in if that was instructive. I did similar things in some of the others.

var dataContext1 = new BASE.data.DataContext(service);
var person = dataContext1.people.createInstance();

dataContext1.dispose();

dataContext2.people.add(person);
dataContext2.saveChangesAsync().try();

dataContext.purgeChangeTracker

Restores all entities to their initial state. Updated and Removed go to loaded; Added go to detached. This doesn't change the entities back to their initial property values.

var person = dataContext.people.createInstance();
var address = dataContext.people.createinstance();

var pendingEntities = dataContext.getPendingEntities().added;

console.log(pendingEntities.length); // 2

dataContext.purgeChangeTracker();

pendingEntities = dataContext.getPendingEntities().added;

console.log(pendingEntities.length); // 0