Using the IndexedDb ObjectStore in Dart - dart

I've been trying to get a single entry from an IndexedDb instance using Dart's IndexedDb package. But I call getObject() which the documentation says returns a Future* (a Future of what I don't know) I don't have any Maps or other objects to work with.
What does the Future return in the store.getObject(id).then() method? If it doesn't return anything, how do I get information stored in the DB?
//within a TurnObjectStore class
Future<Turn> getTurn(int id){
Transaction t = _db.transaction(_TABLE_NAME, 'readonly');
ObjectStore store = t.objectStore(_TABLE_NAME);
return store.getObject(id).then((Map turn){
print('getTurn');
print(turn);
return turn;
});
}
A similar question is answered here indexed_db getObject() - how to return result but I'm getting warnings in my IDE when I try it that way and I'm wondering if the spec has changed since last year.
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-dom-indexed_db.ObjectStore#id_getObject

I am using Dart 1.2 and getObject(id) works fine.
I believe that the returned Future type depends on what is stored into your DB...
If you did save the data as a Map of your object, it will return a Map.
To store&restore your object as a Map, you can look at the official dart example for indexedDB:
// Serialize this to an object (a Map) to insert into the database.
Map toRaw() {
return {
'milestoneName': milestoneName,
'happensOn': happensOn.toString(),
};
}
//Create a Milestone based on the Map returned by the IndexedDB
Milestone.fromRaw(key, Map value):
dbKey = key,
milestoneName = value['milestoneName'],
happensOn = DateTime.parse(value['happensOn']) {
tick();
}
You can see the stored object directly from the developers tools of chrome, for a stored Map it should looks like this:
So i can get back the object with getObject:
var tr = mydb.transaction(myDB.CLIENT_STORE, 'readonly');
var st = tr.objectStore(myDB.CLIENT_STORE);
st.getObject(2).then((r){
Client client = new Client.fromRaw(2,r);
[...]
}

Related

How to get multiple RecordSets from StoredProcedure with NReco.Data in Asp.NetCore

I use NReco.Data in my Asp.NetCore Application to make db-calls, because I don't want to use EF and DataTable isn't supported yet.
Now I need to call a StoredProcedure and get Multiple RecordSets (or Dictionarylists).
At the moment I called this:
dbAdapter.Select($"STOREDNAME #{nameof(SQLPARAMETER)}", SQLPARAMETER).ToRecordSet()
But the stored gives me more than 1 recordset, can anyone help me to get the others?
Currently NReco.Data.DbDataAdapter has no API for processing multiple result sets returned by single IDbCommand.
You can compose IDbCommand by yourself, execute data reader and read multiple result sets in the following way:
IDbCommand spCmd; // lets assume that this is DB command for 'STOREDNAME'
RecordSet rs1 = null;
RecordSet rs2 = null;
spCmd.Connection.Open();
try {
using (var rdr = spCmd.ExecuteReader()) {
rs1 = RecordSet.FromReader(rdr);
if (rdr.NextResult())
rs2 = RecordSet.FromReader(rdr);
}
} finally {
spCmd.Connection.Close();
}
As NReco.Data author I think that support for multiple result sets may be easily added to DbDataAdapter API (I've just created an issue for that on github).
-- UPDATE --
Starting from NReco.Data v.1.0.2 it is possible to handle multiple result sets in the following way:
(var companies, var contacts) = DbAdapter.Select("exec STOREDNAME").ExecuteReader(
(rdr) => {
var companiesRes = new DataReaderResult(rdr).ToList<CompanyModel>();
rdr.NextResult();
var contactsRes = new DataReaderResult(rdr).ToList<ContactModel>();
return (companiesRes, contactsRes);
});
In the same manner DataReaderResult can map results to dictionaries or RecordSet if needed.

Breeze - How to Load Navigation property from cache

I am getting a single entity by using a method fetchEntityByKey, after that I am loading navigation property for the entity by entityAspect.loadNavigationProperty. But loadNavigationProperty always make a call to the server, what I am wondering if I can first check it from cache, if it is exist then get it from there otherwise go the server. How is it possible? Here is my current code
return datacontext.getProjectById(projectId)
.then(function (data) {
vm.project = data;
vm.project.entityAspect.loadNavigationProperty('messages');
});
Here is a function that I encapsulated inside datacontext service.
function getProjectById(projectId) {
return manager.fetchEntityByKey('Project', projectId)
.then(querySucceeded, _queryFailed);
function querySucceeded(data) {
return data.entity;
}
}
Also, how is it possible to load navigation property with some limit. I don't want to have all records for navigation property at once for performance reason.
You can use the EntityQuery.fromEntityNavigation method to construct a query based on an entity and a navigationProperty . From there you can execute the resulting query locally, via the EntityManager.executeQueryLocally method. So in your example once you have a 'project' entity you can do the following.
var messagesNavProp = project.entityType.getProperty("messages");
var query = EntityQuery.fromEntityNavigation(project, messagesNavProp);
var messages = myEntityManager.executeQueryLocally(query);
You can also make use of the the EntityQuery.using method to toggle a query between remote and local execution, like this:
query = query.using(FetchStrategy.FromLocalCache);
vs
query = query.using(FetchStrategy.FromServer);
please take a look here: http://www.breezejs.com/sites/all/apidocs/classes/EntityManager.html
as you can see fetchEntityByKey ( typeName keyValues checkLocalCacheFirst ) also have a third optional param that you can use to tell breeze to first check the manager cache for that entity
hope this helps

How do I set a property on a breeze entity client-side?

I've tried drilling down into the object and looking at the docs but haven't found anything. I've created an entity and I need to assign some properties manually. I see _backingStore and entityAspect on the object... and I know the property names but don't know how to set them via the breeze entity.
In case it matters, I'm creating a new object and then copying properties over from another object to facilitate cloning.
function createDocument() {
var manager = datacontext.manager;
var ds = datacontext.serviceName;
if (!manager.metadataStore.hasMetadataFor(ds)) {
manager.fetchMetadata(ds).then(function () {
return manager.createEntity("Document");
})
}
else {
return manager.createEntity("Document");
}
}
function cloneDocument(doc) {
var clonedDocument = createDocument();
// Copy Properties Here - how?
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
Not knowing what your properties might be, here are two scenarios -
function cloneDocument(doc) {
var clonedDocument = createDocument();
clonedDocument.docId(doc.docId());
clonedDocument.description(doc.description());
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
There are a few things to note here - I am assuming you are using Knockout and needing to set the properties. If you are not using Knockout then you can remove the parans and use equals -
clonedDocument.docId = doc.docId;
I believe this is true for if you are not using Knockout (vanilla js) and if you are using Angular, but I have not used Breeze with Angular yet so bear with me.
And here's another way that works regardless of model library (Angular or KO)
function cloneDocument(doc) {
var manager = doc.entityAspect.entityManager; // get it from the source
// Check this out! I'm using an object initializer!
var clonedDocument = manager.createEntity("Document", {
description: doc.description,
foo: doc.foo,
bar: doc.bar,
baz: doc.baz
});
return clonedDocument;
}
But beware of this:
clonedDocument.docId = doc.docId; // Probably won't work!
Two entities of the same type in the same manager cannot have the same key.
Extra credit: write a utility that copies the properties of one entity to another without copying entityAspect or the key (the id) and optionally clones the entities of a dependent navigation (e.g., the order line items of an order).

Breeze registerEntityTypeCtor not working

I am working on a Hottowel project and I want to format some data passed on from database to Breeze, but it looks like the ctor is not getting registered.
What am I doing wrong?
In datacontext.js:
var manager = configureManager();
function configureManager() {
var mng = new breeze.EntityManager('breeze/data');
breeze.NamingConvention.camelCase.setAsDefault();
model.configureMetadataStore(mng.metadataStore);
return mng;
}
In model.js:
function configureMetadataStore(metadataStore) {
metadataStore.registerEntityTypeCtor
('UserInfo', null, userInfoInitializer);
}
function userInfoInitializer() {
if (this.Email == "")
this.Email = '---';
this.CreateDate = formatDateTime(this.CreateDate);
}
function formatDateTime(DateTime) {
return moment.utc(DateTime).format('DD. MM. YYYY. [у] hh:mm');
}
Datacontext has a reference to model, the data is transferred from database and appears on the screen, but is not formatted. console.log() calls from userInfoInitializer() are not appearing.
When I am constructing an entity, my constructor needs to have an entity to construct. I have not tried your above code but I believe that Breeze passes an entity in and you need to give that entity properties. Using this. MAY work but it is the first thing that stands out to me.
function userInfoInitializer(user) {
if (user.Email == "")
user.Email = "---";
2nd - What is your entity named in your model? Is it UserInfo or just User? You probably already know this but you need to make sure when you are adding a constructor you use the properly named Entity.
3rd - If you are using camelCase then you need to leave the first letter of the property lowercase. Ex . user.email and user.createDate.
Last, I can't tell if you are creating a 'createDate' in the constructor or that is being passed from your model. If it is indeed a property you are creating I would recommend making it an knockout observable or computed property. If it is coming from the database then you need to do something like
if (!user.createDate) { } //set it
Remember that all of the entities being returned from the database will be given that property, so if you have entities that already have a createDate in your example you are overriding that date. If you want to set createDate to now then I would move that into your method where you are creating the object.

Json and Circular Reference Exception

I have an object which has a circular reference to another object. Given the relationship between these objects this is the right design.
To Illustrate
Machine => Customer => Machine
As is expected I run into an issue when I try to use Json to serialize a machine or customer object. What I am unsure of is how to resolve this issue as I don't want to break the relationship between the Machine and Customer objects. What are the options for resolving this issue?
Edit
Presently I am using Json method provided by the Controller base class. So the serialization I am doing is as basic as:
Json(machineForm);
Update:
Do not try to use NonSerializedAttribute, as the JavaScriptSerializer apparently ignores it.
Instead, use the ScriptIgnoreAttribute in System.Web.Script.Serialization.
public class Machine
{
public string Customer { get; set; }
// Other members
// ...
}
public class Customer
{
[ScriptIgnore]
public Machine Machine { get; set; } // Parent reference?
// Other members
// ...
}
This way, when you toss a Machine into the Json method, it will traverse the relationship from Machine to Customer but will not try to go back from Customer to Machine.
The relationship is still there for your code to do as it pleases with, but the JavaScriptSerializer (used by the Json method) will ignore it.
I'm answering this despite its age because it is the 3rd result (currently) from Google for "json.encode circular reference" and although I don't agree with the answers (completely) above, in that using the ScriptIgnoreAttribute assumes that you won't anywhere in your code want to traverse the relationship in the other direction for some JSON. I don't believe in locking down your model because of one use case.
It did inspire me to use this simple solution.
Since you're working in a View in MVC, you have the Model and you want to simply assign the Model to the ViewData.Model within your controller, go ahead and use a LINQ query within your View to flatten the data nicely removing the offending circular reference for the particular JSON you want like this:
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other Machine properties you desire
Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire
}};
return Json(jsonMachines);
Or if the Machine -> Customer relationship is 1..* -> * then try:
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other machine properties you desire
Customers = new List<Customer>(
(from c in m.Customers
select new Customer()
{
Id = c.Id,
Name = c.Name,
// Other Customer properties you desire
}).Cast<Customer>())
};
return Json(jsonMachines);
Based on txl's answer you have to
disable lazy loading and proxy creation and you can use the normal methods to get your data.
Example:
//Retrieve Items with Json:
public JsonResult Search(string id = "")
{
db.Configuration.LazyLoadingEnabled = false;
db.Configuration.ProxyCreationEnabled = false;
var res = db.Table.Where(a => a.Name.Contains(id)).Take(8);
return Json(res, JsonRequestBehavior.AllowGet);
}
Use to have the same problem. I have created a simple extension method, that "flattens" L2E objects into an IDictionary. An IDictionary is serialized correctly by the JavaScriptSerializer. The resulting Json is the same as directly serializing the object.
Since I limit the level of serialization, circular references are avoided. It also will not include 1->n linked tables (Entitysets).
private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) {
var result = new Dictionary<string, object>();
var myType = data.GetType();
var myAssembly = myType.Assembly;
var props = myType.GetProperties();
foreach (var prop in props) {
// Remove EntityKey etc.
if (prop.Name.StartsWith("Entity")) {
continue;
}
if (prop.Name.EndsWith("Reference")) {
continue;
}
// Do not include lookups to linked tables
Type typeOfProp = prop.PropertyType;
if (typeOfProp.Name.StartsWith("EntityCollection")) {
continue;
}
// If the type is from my assembly == custom type
// include it, but flattened
if (typeOfProp.Assembly == myAssembly) {
if (currLevel < maxLevel) {
result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1));
}
} else {
result.Add(prop.Name, prop.GetValue(data, null));
}
}
return result;
}
public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) {
return JsonFlatten(data, maxLevel, 1);
}
My Action method looks like this:
public JsonResult AsJson(int id) {
var data = Find(id);
var result = this.JsonFlatten(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
In the Entity Framework version 4, there is an option available: ObjectContextOptions.LazyLoadingEnabled
Setting it to false should avoid the 'circular reference' issue. However, you will have to explicitly load the navigation properties that you want to include.
see: http://msdn.microsoft.com/en-us/library/bb896272.aspx
Since, to my knowledge, you cannot serialize object references, but only copies you could try employing a bit of a dirty hack that goes something like this:
Customer should serialize its Machine reference as the machine's id
When you deserialize the json code you can then run a simple function on top of it that transforms those id's into proper references.
You need to decide which is the "root" object. Say the machine is the root, then the customer is a sub-object of machine. When you serialise machine, it will serialise the customer as a sub-object in the JSON, and when the customer is serialised, it will NOT serialise it's back-reference to the machine. When your code deserialises the machine, it will deserialise the machine's customer sub-object and reinstate the back-reference from the customer to the machine.
Most serialisation libraries provide some kind of hook to modify how deserialisation is performed for each class. You'd need to use that hook to modify deserialisation for the machine class to reinstate the backreference in the machine's customer. Exactly what that hook is depends on the JSON library you are using.
I've had the same problem this week as well, and could not use anonymous types because I needed to implement an interface asking for a List<MyType>. After making a diagram showing all relationships with navigability, I found out that MyType had a bidirectional relationship with MyObject which caused this circular reference, since they both saved each other.
After deciding that MyObject did not really need to know MyType, and thereby making it a unidirectional relationship this problem was solved.
What I have done is a bit radical, but I don't need the property, which makes the nasty circular-reference-causing error, so I have set it to null before serializing.
SessionTickets result = GetTicketsSession();
foreach(var r in result.Tickets)
{
r.TicketTypes = null; //those two were creating the problem
r.SelectedTicketType = null;
}
return Json(result);
If you really need your properties, you can create a viewmodel which does not hold circular references, but maybe keeps some Id of the important element, that you could use later for restoring the original value.

Resources