Breeze - Can't Create Entity when PK is an autogenerated ID - oracle

I have used breeze's CreateEntity a few times when table's PK is a user-entered value. And a few times with SQL SERVER when PK is an IDENTITY. This is my first time trying to do it when PK is autogenerated ID (actually a "sequence") in ORACLE. It isn't working.
I do check first to make sure I have fetched the Metadata then create the new, empty entity that will be filled in with values by user.
My code to createEntity (newEntity is a knockout Observable):
function createEntity(newEntity) {
newEntity(manager.createEntity(entityNames.escctransactions, {})); <<<<< this fails
return;
}
The Error:
Cannot attach an object of type (ESCC_TRANSACTIONS:... ) to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'
I know I need to set the AutoGeneratedKeyType to "Identity" but not sure how to do it. Tried this when I'm inititalizing the metadata, but still getting same error so it's obviously not working:
var entyType = manager.metadataStore.getEntityType("ESCC_TRANSACTIONS");
entyType.setProperties({ AutoGeneratedKeyType: AutoGeneratedKeyType.Identity });
I've seen something about doing it in a constructor but I've never used a constructor in JavaScript. Also something about changing it in a config?
Using Breeze 1.6, Knockout.js 3.4, .NET 4.5.2 framework
THANKS

Figured it out myself and it's working now. The code to set AutoGeneratedKeyType is as follows:
var entityType = manager.metadataStore.getEntityType("ESCC_TRANSACTIONS");
entityType.autoGeneratedKeyType = "Identity";
Or this works:
var entityType = manager.metadataStore.getEntityType("ESCC_TRANSACTIONS");
entityType.autoGeneratedKeyType = breeze.AutoGeneratedKeyType.Identity;
And in spite of the Breeze documentation for AutoGeneratedKeyType here:
http://breeze.github.io/doc-js/api-docs/classes/AutoGeneratedKeyType.html, it's not a capital "A" in Auto, it's a small "a".

Related

Entity being tracked despite AsNoTracking

I have an object, Client, with a navigation property that is a list of Order objects. Whenever I retrieve a Client object, I include the list of Orders, with AsNoTracking().
public new IQueryable<Client> FindByConditionNoTracking(Expression<Func<Client, bool>> expression)
{
return this.ClientContext.Set<Client>().Include(s => s.Orders)
.Where(expression).AsNoTracking();
}
In my UpdateClient repository method, I take in a Client object. I then attempt to retrieve that original client from the database (using Include to get the child Orders), map the Client param to the original, and save to the database. Over here, I do not use AsNoTracking, because I specifically want the changes to be tracked.
public new void Update(Client client)
{
var id = client.ClientId;
var original = this.ClientContext.Clients.Include(s => s.Orders).Where(s => s.ClientId == id)
.FirstOrDefault<Client>();
original = _mapper.Map(client, original);
this.ClientContext.Update(original);
}
The error I am getting is that an instance of Order with the same key value is already being tracked. A few problems with that:
Wherever the Client and the child Orders are retrieved for the purposes of display I use AsNoTracking.
The only place where I retrieve without AsNoTracking is where I get the original within this very method.
The bug isn't with the parent property. If I was improperly retrieving the Client elsewhere, wouldn't I have this error with the Client id itself? But the error seems to be only with the navigation property.
All insight is appreciated!
If anyone else runs into this: Automapper, when mapping collections, apparently recreates the entire collection. I solved the above issue by using Automapper.Collections in my mapping configuration. Thanks to Mat J for the tip!

Parse-Server prevent fields from being added automatically

Right now, if I add a field to a Parse object and then save it, the new column shows up in the Parse dashboard.
For example, after running:
let media = new Parse.Object("Media");
media.set("foo", "bar");
await media.save();
I will have a new column called foo.
Is it possible to prevent this from happening?
Yes. This can be done using class-level permissions, which allow you to prevent fields being added to classes.
Parse lets you specify what operations are allowed per class. This lets you restrict the ways in which clients can access or modify your classes.
...
Add fields: Parse classes have schemas that are inferred when objects are created. While you’re developing your app, this is great, because you can add a new field to your object without having to make any changes on the backend. But once you ship your app, it’s very rare to need to add new fields to your classes automatically. You should pretty much always turn off this permission for all of your classes when you submit your app to the public.
You would have to add a beforeSave trigger for every one of your classes, keep a schema of all your keys, iterate over the request.object's keys, and see if there are any that do not belong in your schema. You can then either un-set them and call response.success(), or you can call response.error() to block the save entirely, preferably with a message indicating the offending field(s).
const approvedFields = ["field1", "field2", "field3"];
Parse.Cloud.beforeSave("MyClass", function(request, response) {
let object = request.object;
for( var key in object.dirtyKeys() ) {
if( approviedFields.indexOf(key) == -1 ) return response.error(`Error: Attempt to save invalid field: ${key});
}
response.success();
});
Edit:
Since this got a little attention, I thought I'd add that you can get the current schema of your class. From the docs: https://docs.parseplatform.org/js/guide/#schema
// create an instance to manage your class
const mySchema = new Parse.Schema('MyClass');
// gets the current schema data
mySchema.get();
It's not clear if that's async or not (you'll have to test yourself, feel free to comment update the answer once you know!)
However, once you have the schema, it has a fields property, which is an object. Check the link for what those look like.
You could validate an object by iterating over it's keys, and seeing if the schema.fields has that property:
Parse.Cloud.beforeSave('MyClass', (request, response) => {
let object = request.object;
for( var key in object.dirtyKeys() ) {
if( !schema.fields.hasOwnProperty(key) ) < Unset or return error >
}
response.success();
}
And an obligatory note for anyone just starting with Parse-Server on the latest version ,the request scheme has changed to no longer use a response object. You just return the result. So, keep that in mind.

How to use a Database Generated Identity Key in Web Api OData

I've managed to create number of readonly Web Api OData services following the tutorials here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api. I'm therefore employing the ODataConventionModel builder to create the model from a set of entities (incidentally coming from a Telerik ORM). This all seems to work fine and I can happily issue queries, view the metadata and so forth on the service.
I've now tried to turn my attention to the other CRUD operations - firstly Create and have stumbled into a problem! Namely, the Post method fires correctly (CreateEntity) but the entity parameter is null - by doing a check against the ModelState.IsValid, it shows that the problem is a null ID (key) value. This is unsurprising because the database uses a Database Generated Identity for the ID column and therefore the ID would be created when the entity is saved into the database context.
I've therefore tried all sorts of ways of marking the ID column as database generated, but haven't managed to find anything. Strangely, I can't seem to find even one post of someone asking for this - surely I can't be the only one?!
I noted that when looking at the EF modelbuilder (for example here: http://forums.asp.net/t/1848984.aspx/1) there appears to be a means of affecting the model builder with a .HasDatabaseGeneratedOption property, but no similar option exists in the System.Web.Http.OData equivalent.
So the questions therefore are:
Is there a means of altering the model builder (or something else) so that the controller will accept the object and deserialize the entity even with a null key value?
If so, how can I do this?
If not, any suggestions as to other options?
I realise that I could potentially just populate the object with an (in this case) integer value from the client request, but this seems a) semantically wrong and b) won't necessarilly always be possible as a result of the client toolkit that might be used.
All help gratefully received!
Many thanks,
J.
You need to create a viewmodel for insert which does not contain the ID parameter. Use Automapper to map the properties of the incoming insert-model to your data entities.
The problem that you're having is that ID is a required attribute in your data model because it is your PK, except during insert, where it shouldn't be specified.
In my case, my database-generated key is a Guid.
As a work-around, in my TypeScript client code, I submit (via http POST) the object with an empty Guid like this: Note: ErrorId is the key column.
let elmahEntry: ELMAH_Error = {
Application: 'PTUnconvCost',
Host: this.serviceConfig.url,
Message: message,
User: that.userService.currentUserEmail,
AllXml: `<info><![CDATA[\r\n\r\n${JSON.stringify(info || {})}\r\n\r\n]]></info>`,
Sequence: 1,
Source: source,
StatusCode: 0,
TimeUtc: new Date(Date.now()),
Type: '',
ErrorId: '00000000-0000-0000-0000-000000000000'
};
Then, in my WebApi OData controller, I check to see if the key is the empty guid, and if so, I replace it with a new Guid, like this:
// POST: odata/ELMAH_Error
public IHttpActionResult Post(ELMAH_Error eLMAH_Error)
{
if (eLMAH_Error.ErrorId == Guid.Empty)
{
eLMAH_Error.ErrorId = Guid.NewGuid();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.ELMAH_Error.Add(eLMAH_Error);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (ELMAH_ErrorExists(eLMAH_Error.ErrorId))
{
return Conflict();
}
else
{
throw;
}
}
return Created(eLMAH_Error);
}

Kendo datasource model - difference between _data[0] and get(0)

I would like to know the difference between
$("#uploadedFile").val(e.files[0].name);
var model = $("#blueprint_listview").data("kendoListView").dataSource.get(0);
model.set("filename", $("#uploadedFile").val());
And
$("#uploadedFile").val(e.files[0].name);
var model = $("#blueprint_listview").data("kendoListView").dataSource._data[0];
model.set("filename", $("#uploadedFile").val());
I am having an editable listview with a upload.
And the above code is written on the success event on the kendo upload.
The second code works fine for insert and update.
However, the first code works fine for insert, but for update it is showing an error which says - "The model is not defined"
I was wondering what could be the reason?
As stated in the documentation, get retrieves a record with the corresponding id. This way, when a new record is inserted it seems that it has the default id of 0, that's why get(0) === _data[0] but when you are updating the listview, a "real" id (>=1) is given to your new line and there is no longer an item with id=0, so model is then null.
On the other side, the internal method _data is an array with all the lines of your list view put in the order of their position in the listview. But if you want to access to this property, the equivalent "public" method is at :
$("#blueprint_listview").data("kendoListView").dataSource._data[0] ===
$("#blueprint_listview").data("kendoListView").dataSource.at(0); // allways true

RIA DomainService + ActiveRecord

I tried to use SubSunsonic.ActiveRecord in SL3 project that uses .NET RIA Services.
However when I try to return some IQuerable in DomainService class I get an error that the classes generated by Subsonic have a property 'Columns' with an unsupported type.
That's what I have
public IEnumerable<SE_NorthWind.SuperEmployee> GetIntegers()
{
return SE_NorthWind.SuperEmployee.All()
.Where(emp => emp.Issues > 100)
.OrderBy(emp => emp.EmployeeID);
}
And this is the error I get
Error 7 Entity 'SE_NorthWind.SuperEmployee' has a property 'Columns' with an unsupported type. SuperEmployee
Any idea what to do? Don't really wanna use Linq to SQL :)
Thx
P.S. Just tried to LinqTemplates from SubSonic, but this solution I get the error
Error 4 The entity 'SE_NorthWind.SuperEmployee' does not have a key defined. Entities exposed by DomainService operations must have must have at least one property marked with the KeyAttribute. SuperEmployee
of course SuperEmployee table has a primary key, cause the classes generated by SubSonic can see it
...
Columns.Add(new DatabaseColumn("EmployeeID", this)
{
IsPrimaryKey = true,
DataType = DbType.Int32,
IsNullable = false,
AutoIncrement = true,
IsForeignKey = false,
MaxLength = 0
});
...
But RIA objects, they need some attributes. I guess I'll have to go with native Linq To SQL until SubSonic adapts to all this :(
To answer the second part of your question.
You need to add the "KeyAttribute" to the PrimaryKey property on the "EmployeeId" property.
The attribute is in the "System.ComponentModel.DataAnnotations" namespace.
No up on Sub Sonic 3, but you could change the underlying template to generate this, or change the sub sonic engine and submit it as a patch.
I'm running with SilverLight 3 with RaiServices.
Hope this helps.
Can you try removing the [EnableClientAccess()] attribute to see if your project will build?

Resources