I noticed that when I add a document to RavenDB and see the "Raven-Entity-Name" metadata it makes it plural. E.g. if my model name was Product it changes it to Products. Why such kind for behavior?
If I have create an index I am forced to use docs.Products
It's part of the philosophy of RavenDB to do convention over configuration, so it does this by default.
But you can override it if you want to, you can do something like this:
_documentStore = new DocumentStore { Url = "http://localhost:8080/" };
_documentStore.Conventions.FindTypeTagName = t =>
{
if (t.Name == "MyClass")
return "MyClassBlahBlah";
else
return Raven.Client.Util.Inflector.Pluralize(t.Name);
};
_documentStore.Initialize();
See this thread on the RavenDB discussion group for more info
Related
I have a SAPUI5 application that uses OData V2.
In one part of the application for deleting of the items in a list I have to close change set after each call.
Then I use the following code:
sGroupId = "dmsch" + new Date().getTime();
oDataModel.setDeferredGroups([sGroupId]);
for (var i = 0; i < aSelectedContexts.length; i++) {
var sObjectPath = aSelectedContexts[i].getPath();
this._deleteObject(sObjectPath, sGroupId, fnAllRequestCompleted, fnAllRequestFailed);
}
oDataModel.submitChanges({
groupId: sGroupId
});
And in the _deleteObject function I set different changeSetId for each request, the b:
_deleteObject: function(sObjectPath, sGroupId, fnSuccessCallBackFunction, fnFailedCallBackFunction) {
var oDataModel = this.getModel();
var sChangeSetId = "cs" + (new Date().getTime() * (1 + Math.random()));
oDataModel.remove(sObjectPath, {
groupId: sGroupId,
changeSetId: sChangeSetId,
......
Now after a successful delete as soon as I create a new entry by using the createEntry function it tries to send the data of that entry to the server.
The question is how can I reset the effect of setDeferredGroups function.
Note: I need to use setDeferredGroups, and I am sure it is reason of sending newly created entries automatically to the server by each change. I need to set the setting of the ODataModel back to its original state.
Note2: Here is something regarding oData Version 4 that explain this automatic behavior after a failure.
The SAP docs here - I've tried to summarize below.
The default change groups are
{"*": {
groupId: "changes"
}
}
And the default deferred groups are
["changes"]
You can reset the data model change groups to default using
oModel.setChangeGroups({"*": {
groupId: "changes"
}
});
oModel.setDeferredGroups(["changes"]);
With this default configuration, all changes to all entity types will be collected in the changes group, and are deferred (not sent to the server automatically).
So oModel.setChangeGroups(...) is how change groups are defined, and oModel.setDeferredGroups is how each of those groups is determined to be deferred or not
The reason I mention the default change groups AND the default deferred groups, is because if not set properly, you may see unexpected behavior when using two way data binding.
For example: removing the default change group by calling oModel.setChangeGroups({}) will result in all changes to all entity types NOT getting collected into any change group, and thus not being deferred. You will see any changes made sent to the server automatically.
So lets say you have an entity type Employee and you want any changes made to this entity type to be collected in one group and be deferred:
var oChangeGroups = oModel.getChangeGroups();
oChangeGroups.Employee = {groupId: "employees"};
oModel.setChangeGroups(oChangeGroups);
var aDeferredGroups = oModel.getDeferredGroups();
aDeferredGroups.push("employees");
oModel.setDeferredGroups(aDeferredGroups);
Now you have two change groups, * with ID changes and Employee with ID employees. Any changes made to any Employee entities will be in the employees group, and all other changes will be in the changes group.
So now any create/delete/update of an employee can be submitted separately from any other changes to other entity types
oModel.createEntry("/EmployeeSet", {
groupId: "employees",
properties: {
name: "New Guy"
}
});
oModel.submitChanges({groupId: "employees"});
From this point, to go back to the default and get rid of the employees change group, you can use what I wrote above to reset everything back to default.
In my table controller, I have:
public IQueryable<MyTable> GetAllMyTable()
I would like to replace the above with my own:
[HttpGet, Route("tables/MyTable")]
public IEnumerable<MyTable> GetAllMyTable()
But I get this response when I call it:
HTTP/1.1 405 Method Not Allowed
Somehow the Web API routing does not reach my method.
Why I'm doing this: the original method produces an inefficient Entity Framework SQL query that takes 3 seconds per call on my local test environment. This is running the query captured from SQL Profiler directly in SQL Mgt Studio. An equivalent query takes less than a second to run. Terrible.
Worse, the inefficient EF queries consumes lots of Azure SQL DTUs, tempting you to up your Azure subscription level if you want a quick fix.
Azure Mobile Apps is wonderful, but the multiple layers of abstraction makes it hard to really see what's going on under the hood, and therefore harder to tune.
Any help would be much appreciated.
HTTP/1.1 405 Method Not Allowed
Per my understanding, the error is obvious. You could send the GET HTTP verb to your endpoint tables/MyTable for retrieving the data. You need to check your request against your mobile app backend via fiddler.
Azure Mobile Apps is wonderful, but the multiple layers of abstraction makes it hard to really see what's going on under the hood, and therefore harder to tune.
For the common table controller, it would look like this:
public IQueryable<Message> GetAllMessage()
{
return Query();
}
The Query() method under EntityDomainManager.cs would equal as follows:
IQueryable<TData> query = this.Context.Set<TData>();
if (!includeDeleted)
{
query = query.Where(item => !item.Deleted);
}
return query;
If it deals with the ODATA queries (e.g. $top, $skip, $filter, etc.), the Nested SQL statement would be generated. We could modify the action to clarify it as follows:
public IEnumerable<Message> GetAllMessage(ODataQueryOptions opt)
{
var message = context.Set<Message>();
var query2=opt.ApplyTo(message, new ODataQuerySettings());
return query2.Cast<Message>().ToList();
}
Here's my rather crude attempt at bypassing the Entity Framework/OData plumbing and using direct SQL. (Wouldn't it be great if Dapper is supported!) This one works well, and is faster than the nested SQL that EF produces. The handling of OData is hacky; I have not had time to investigate using OData to extract the values for UpdatedAt, skip, and top.
I'm only using this approach for one method that needs optimisation. This is the method that the Azure Mobile App client calls when doing a pull.
public IEnumerable<MyTable> GetAllMyTable()
{
var qryValues = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var updatedAtFilter = qryValues["$filter"];
var skip = qryValues["$skip"];
var top = qryValues["$top"];
if (updatedAtFilter != null)
{
var r = new Regex(#"^.+datetimeoffset'(?<time>.+)'.+$", RegexOptions.None);
var m = r.Match(updatedAtFilter);
if (m.Success)
{
var updatedAt = m.Groups["time"].Value.Replace("T", " ");
var sqlString = #"SELECT T0.*
FROM MyTable T0
WHERE T0.UpdatedAt >= #UpdatedAt
ORDER BY UpdatedAt, Id
OFFSET #Skip ROWS
FETCH NEXT #Top ROWS ONLY";
var updatedAtParam = new SqlParameter("UpdatedAt", SqlDbType.DateTimeOffset);
updatedAtParam.Value = updatedAt;
var skipParam = new SqlParameter("Skip", SqlDbType.Int);
skipParam.Value = int.Parse(skip);
var topParam = new SqlParameter("Top", SqlDbType.Int);
topParam.Value = int.Parse(top);
var data = _context.Database.SqlQuery<MyTable>(sqlString, new object[] { updatedAtParam, skipParam, topParam }).AsEnumerable<MyTable>();
return data;
}
}
return null;
}
I think I am missing something in my understanding of tracking property value changes in entity framework.
I have an application where i store service requests. Whenever a team value in changed in the service request record, I want to create a team history record in a related teamhistory entity.
I have created the app in MVC using the standard scaffolding for controllers and views.
In the (post)edit task in the controller, the standard logic generated has the following code
if (ModelState.IsValid)
{
db.Entry(serviceRequest).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(serviceRequest);
I have modified that to include the creating of the teamhistory record and an individualhistory record for individual assigned within team. The code for creating these related records work, BUT i want these records only created when the values on team or member(individual) change from what they were previously.
So far the conditions i have specified due not trigger this correctly because I havent gotten the condition right. Below is the current code:
//string teamorig = db.Entry(serviceRequest).Property(u => u.Team).OriginalValue.ToString();
//string teamcurr = db.Entry(serviceRequest).Property(u => u.Team).CurrentValue.ToString();
//if (teamorig != teamcurr)
var TeamIsModified = db.Entry(serviceRequest).Property(u => u.Team).IsModified;
if (TeamIsModified)
{
serviceRequest.TeamAssignmentHistories.Add(new TeamAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
Team = serviceRequest.Team
});
}
//=========================================================================================
// if individual assigned has changed add individual history record========================
var IndividualIsModified = db.Entry(serviceRequest).Property(u => u.Member).IsModified;
if (IndividualIsModified)
{
serviceRequest.IndividualAssignmentHistories.Add(new IndividualAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
AssignedTo = serviceRequest.Member.Value,
});
}
//===========================================================================================
The var teamismodified logic doesnt work. When I save the page without making any changes on it- the logic kicks off because in debugging it thinks the field has been modified.
When I comment out that code and uncomment the code above it for original and currentvalues- ie the teamorig and teamcurr logic, teamcurr and teamorig have the same values in debug, even when they have been forced into a change on the save in the MVC view page. Because they have the same values, the if condition is false so the team history record is not created.
The above code has been sandwiched in between
db.Entry(serviceRequest).State = EntityState.Modified;
and
await db.SaveChangesAsync();
statements.
What am I not understanding about entity framework tracking changes in mvc? Why does think its modified when i make not changes to team, and why are teamorig and teamcurr the same when I do make the changes?
Any advice would be welcome. Thanks
I have a web application with an Angular / Breeze client side calling into a Breeze Web API, which uses an Entity Framework code first model. I have a datacontext (Angular service) responsible for all communications with server.
I would like to completely separate the server development from the client side development so developers need not even have .NET installed on their system. I would like the solution to require very little coding in way of creating fakes, because the app is changing frequently and I do not want to have to rewrite fakes every time my implementation changes. I have a bunch of test data in the database that I would like to make available on the client.
What is a good way (standard way?) to achieve this?
Just create mocks. You don't even have to make a RESTful call if you don't want to, just have your service decide whether to hit the server or pull from cache and load up your cache locally on start -
function loadMocks (manager) {
var personMockOne = manager.createEntity('Person', { id: 1, firstName: 'John', lastName: 'Smith' });
var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' });
companyMockOne.employees.push(personMockOne);
}
http://pwkad.wordpress.com/2014/02/02/creating-mocks-with-breeze-js/
To Expand...
Doing this requires a bit of extra set up. I personally always write my queries separate from my controller / view model logic through a service which takes parameters. A few example parameters are always something like parameters and forceRemote. The idea is that when you go to execute the query you can decide whether to hit the server or query locally. A quick example -
function queryHereOrThere (manager, parameters, forceRemote) {
var query = breeze.EntityQuery().from('EntityName').using(manager);
query.where(parameters);
if (!forceRemote) {
query.executeQueryLocally();
} else {
query.executeQuery();
}
}
Here is my current solution.
Get data from the server with a 'unit test' that creates a Breeze Web API controller and uses it to gather the breeze metadata and all the test data from the database, then writes that data to testData.json and breezeMetadata.json.
Abstract the creation of the Breeze Entity Manager to an Angular service entityManager.
Create a fakeEntityManager Angular service, which: 1) creates the entity manager, 2) overrides the EntityManager.executeQuery function to always use the local version, and 3) loads up the mgr with the test data. The code for that service is below.
In the datacontext service, use the $injector service to conditionally inject a real or a fake entity manager.
datacontext.js
angular.module('app').factory('datacontext', ['$injector','config', datacontext]);
function datacontext($injector, config) {
if (config.useLocalData === true) {
var mgr = $injector.get('fakeEntityManager');
} else var mgr = $injector.get('entityManager');
...
fakeEntityManager.js
(function() {
'use strict';
var serviceId = 'fakeEntityManager';
angular.module('app').factory(serviceId, ['breeze', 'common', em]);
function em(breeze, common) {
var $q = common.$q;
var mgr = getMgr();
populateManager(["Projects", "People", "Organizations"]);
return mgr;
function getMgr() {
breeze.EntityManager.prototype.executeQuery = function(query) {
return $q.when(this.executeQueryLocally(query)).then(function (results) {
var data = {
results: results
};
if (query.inlineCountEnabled == true) data.inlineCount = results.length;
return data;
});
};
var metaData = < PASTE JSON HERE >
new breeze.ValidationOptions({ validateOnAttach: false }).setAsDefault();
var metadataStore = new breeze.MetadataStore();
metadataStore.importMetadata(metaData, true);
return new breeze.EntityManager({
dataService: new breeze.DataService(
{
serviceName: "fakeApi",
hasServerMetadata: false // don't ask the server for metadata
}),
metadataStore: metadataStore
});
}
function populateManager(resources) {
var testData = < PASTE JSON HERE >;
resources.forEach(function (resource) {
testData[resource].forEach(function (entity) {
mgr.createEntity(mgr.metadataStore.getEntityTypeNameForResourceName(resource), entity);
});
});
}
}
})();
If you don't use inlineCount queries there is no need to override executeQuery. You can just add the following property to the EntityManager constructor's parameter:
queryOptions: new breeze.QueryOptions({ fetchStrategy: breeze.FetchStrategy.FromLocalCache })
Todo: Override the EntityManager.saveChanges() function (or somehow configure the entity manager) to prevent calls to the server while still allowing entities to be edited and saved locally.
I'm scratching my head here, as usual it seems with node projects, and I'm not sure if I'm doing something wrong or if I've run into a bug.
I've got a schema of Server that can have any number of embedded docs called services. I'm running into a problem though where, even though I've successfully removed the individual service from the server object, when I tell it to save it doesn't remove it from the database. The save function is working because it's saving any changes I've made and is also pushing in new embedded docs, it's just not removing one that are already there.
Here is a relatively simplified example of my code:
app.put('/server/:id', function(req, res, next){
app.Server.findOne({_id: req.params.id}, function(err, server) {
server.updated = new Date();
...
for (var num = _.size(req.body.server.services) - 1; num >= 0; num--){
// Is this a new service or an existing one
if (server.services[num]) {
// Is it marked for deletion? If so, delete it
if (req.body.server.services[num].delete == "true") {
server.services[num].remove()
} else { // else, update it
server.services[num].type = req.body.server.services[num].type
...
}
} else {
// It's new, add it
delete req.body.server.services[num]["delete"]
server.services.push(req.body.server.services[num]);
}
}
server.save(function(err){
if (!err) {
req.flash('success', 'Server updated')
} else {
req.flash('error', 'Err, Something broke when we tried to save your server. Sorry!')
console.log(err)
}
res.redirect('/')
});
})
});
So the remove() is actually removing the service. If I do a server.toObject() before the save, it's not there. Any ideas why it wouldn't be removing it from the database when it saves?
Edit: I suppose the version numbers would be helpful. node#0.4.2, mongoose#1.1.5 express#2.0.0rc
I could be wrong, since I've not tested your example, but this sounds like Mongoose isn't detecting that the embedded document is modified.
From the schema types documentation page:
Since it is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect/save those changes. To "tell" Mongoose that the value of a Mixed type has changed, call the .markModified(path) method of the document passing the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
So you answer might be as simple as using the markModified() function.
I found a way to temporary fix this problem.
What I did is load the embedded documents into an array, splice the one to be deleted and replace the array. Something like this:
var oldusers = dl.users;
oldusers.splice(dl.users.indexOf(req.currentUser.id), 1);
dl.users = oldusers;
dl.save(function(err) {...
I know that depending on the size of the document it will