fixupKeys() function in breeze.debug.js - breeze

function fixupKeys(em, keyMappings) {
keyMappings.forEach(function (km) {
var group = em._entityGroupMap[km.entityTypeName];
group._fixupKey(km.tempValue, km.realValue);
});
}
For one of my entities, I am seeing group return undefined, and so throws an error with group._fixupKey().
It looks to me that the km.entityTypeName stores the short name of the entity, but the em._entityGroupMap stores a concatenated name of the entity with it's the namespace.
Anyone know if this a breezejs bug? Or am I doing something wrong in my model that is responsible for this mismatched key?

It was an issue with the implementation of my custom breeze dataservice adapter. Thanks for looking at it with me.

Related

How to delete tenant completely?

I'm developing a multitenant application and would like to have to option to remove a tenant. This however seems to be less trivial than one would assume.
My goal is to delete all references to the tenant everywhere in the database. I understand that Tenant is Soft-Delete, but I since I don't want my database to fill up with old meaningless data I've tried disabling the soft-delete filter.
Here is some code that I've tried:
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
{
await TenantRepository.DeleteAsync(x => x.Id == tenantId);
}
This did not work. The tenant is marked as "IsDeleted" but not removed.
Then I figured that maybe it has something to do with UnitOfWork so I made sure no UnitOfWork was active and then manually controlled it:
using (var unitOfWork = _unitOfWorkManager.Begin())
{
// the codeblock above went here
unitOfWork.Complete();
}
This did not work, same result. And this is just the AbpTenant table. I'm also trying to delete from all other tables. For example AbpSettings and AbpLanguages. It's very unclear to me how to do that at all - the "managers" doesn't contain any Delete functions.
I tried creating IRepository for these entities but it does not work. The error reads
The type Abo.Configuration.Setting cannot be used as a type parameter TEntity in the generic type or method IRepository. There is no implicit reference conversion from Abp.Configuration.Setting to Abo.Domain.Entities.IEntity.
That leaves me with the option to use the DataContext directly:
using (EntityFramework.MyDbContext db = new EntityFramework.MyDbContext())
{
List<PermissionSetting> perms = await db.Permissions.Where(x => x.TenantId == tenantId).ToListAsync();
for (int i=0; i<perms.Count(); i++)
{
db.Permissions.Remove(perms[i]);
}
// I also tried deleting them in bulk at first
// ((DbSet<PermissionSetting>)db.Permissions).RemoveRange(db.Permissions.Where(x => x.TenantId == tenantId));
await db.SaveChangesAsync();
}
I tried that with and without UnitOfWork.
But it simply does not get deleted from the database. I'm getting no errors or Exceptions.
Why does it not get deleted? How can I delete it? Surely it must be possible?
since I don't want my database to fill up with old meaningless data I've tried disabling the soft-delete filter.
From the question on Disable SoftDelete for AbpUserRole:
protected override void CancelDeletionForSoftDelete(EntityEntry entry)
{
if (IsSoftDeleteFilterEnabled)
{
base.CancelDeletionForSoftDelete(entry);
}
}
The type Abo.Configuration.Setting cannot be used as a type parameter TEntity in the generic type or method IRepository. There is no implicit reference conversion from Abp.Configuration.Setting to Abo.Domain.Entities.IEntity.
Inject IRepository<Setting, long> instead of IRepository<Setting>.
That leaves me with the option to use the DataContext directly
...
But it simply does not get deleted from the database. I'm getting no errors or Exceptions.
From the documentation on Data Filters:
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
using (var db = new ...)
{
// ...
}
}
That said, there is no way to easily delete related tenant data completely. Consider writing SQL.

how do I get non-server values mapped back from client?

I am setting properties in client-side Breeze entities. I see the EntityInfo UnmappedValuesMap collection, which I'd like to use. It would be great if I could get a few custom client values returned in the Unmapped collection to avoid adding these everywhere.
I am initializing the metastore with:
store.registerEntityTypeCtor("UserInfo", null, userInfoInitializer);
function userInfoInitializer(userinfo) {
userinfo.creatingId = ko.observable(0);
...
I was hoping 'creatingId' would get passed to server. But nothing extra appears in the net traffic.
I don't think it matters, but on the server I am using Breeze.ContextProvider.
Are there flags somewhere that govern this behavior? Thanks for any guidance.
If you wanted to add a 'creatingId' to every entity type you could do something like this:
metadataStore.getEntityTypes().forEach(
function(entityType) {
var ctor = function () {
this.creatingId = 0;
};
metadataStore.registerEntityTypeCtor(entityType.name, ctor, null);
});
Note- using a constructor instead of an initializer. This will ensure the value appears in the unmapped values collection. Don't worry, breeze will make this an observable property on your entity.

breeze: creating inheritance in client-side model

I'm having a weird issue with the configureMetadataStore.
My model:
class SourceMaterial {
List<Job> Jobs {get; set;}
}
class Job {
public SourceMaterial SourceMaterial {get; set;}
}
class JobEditing : Job {}
class JobTranslation: Job {}
Module for configuring Job entities:
angular.module('cdt.request.model').factory('jobModel', ['breeze', 'dataService', 'entityService', modelFunc]);
function modelFunc(breeze, dataService, entityService) {
function Ctor() {
}
Ctor.extend = function (modelCtor) {
modelCtor.prototype = new Ctor();
modelCtor.prototype.constructor = modelCtor;
};
Ctor.prototype._configureMetadataStore = _configureMetadataStore;
return Ctor;
// constructor
function jobCtor() {
this.isScreenDeleted = null;
}
function _configureMetadataStore(entityName, metadataStore) {
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
}
function jobInitializer(job) { /* do stuff here */ }
}
Module for configuring JobEditing entities:
angular.module('cdt.request.model').factory(jobEditingModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobEditing', metadataStore)
}
}
Module for configuring JobTranslation entities:
angular.module('cdt.request.model').factory(jobTranslationModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobTranslation', metadataStore)
}
}
Then Models are configured like this :
JobEditingModel.configureMetadataStore(dataService.manager.metadataStore);
JobTranslationModel.configureMetadataStore(dataService.manager.metadataStore);
Now when I call createEntity for a JobEditing, the instance is created and at some point, breeze calls setNpValue and adds the newly created Job to the np SourceMaterial.
That's all fine, except that it is added twice !
It happens when rawAccessorFn(newValue); is called. In fact it is called twice.
And if I add a new type of job (hence I register a new type with the metadataStore), then the new Job is added three times to the np.
I can't see what I'm doing wrong. Can anyone help ?
EDIT
I've noticed that if I change:
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
to
metadataStore.registerEntityTypeCtor(entityName, null, jobInitializer);
Then everything works fine again ! So the problem is registering the same jobCtor function. Should that not be possible ?
Our Bad
Let's start with a Breeze bug, recently discovered, in the Breeze "backingStore" model library adapter.
There's a part of that adapter which is responsible for rewriting data properties of the entity constructor so that they become observable and self-validating and it kicks in when register a type with registerEntityTypeCtor.
It tries to keep track of which properties it has rewritten. The bug is that it records the fact of rewrite on the EntityType rather than on the constructor function. Consequently, every time you registered a new type, it failed to realize that it had already rewritten the properties of the base Job type and re-wrapped the property.
This was happening to you. Every derived type that you registered re-wrapped/re-wrote the properties of the base type (and of its base type, etc).
In your example, a base class Job property would be re-written 3 times and its inner logic executed 3 times if you registered three of its sub-types. And the problem disappeared when you stopped registering constructors of sub-types.
We're working on a revised Breeze "backingStore" model library adapter that won't have this problem and, coincidentally, will behave better in test scenarios (that's how we found the bug in the first place).
Your Bad?
Wow that's some hairy code you've got there. Why so complicated? In particular, why are you adding a one-time MetadataStore configuration to the prototypes of entity constructor functions?
I must be missing something. The code to register types is usually much smaller and simpler. I get that you want to put each type in its own file and have it self-register. The cost of that (as you've written it) is enormous bulk and complexity. Please reconsider your approach. Take a look at other Breeze samples, Zza-Node-Mongo for example.
Thanks for reporting the issue. Hang in there with us. A fix should be arriving soon ... I hope in the next release.

breezejs: how to access enum metadata

I've noticed that in the metadata there's an object entityType but also an object enumType.
We use manager.metadataStore.getEntityType() to access the metadata of an Entity.
How can we do it for a given enum ? How would I create the enum on the client side out of the metadata ?
Also, when I assign an enum value to a property, I'd like to to it by name instead of by value.
For instance, assuming that Status is of type myEnum:
myEntity.Status = myEnum.Valid;
instead of
myEntity.Status = 1;
Does breeze have any helper function to access the values of an enum ?
This issue is still open as I write. But you might want to take a look at the work-around described in the answer to this SO question.
I am assuming that you are talking about data properties that are defined as .NET enums on the server, and you want additional metadata about these properties to be made available on the Breeze client.
Unfortunately, Breeze does not yet support any metadata on enum types other than the name of the .NET type backing the enum value. This is the 'enumType' property that will appear on any dataProperty that is backed by an .NET Enum on the server. (We do need to document this better)
Please add a feature request for this to the Breeze User Voice. It's a good idea and we do take these suggestions very seriously.
Well this is not exact solution to your question but definitely can help people who are generating metadata offline.
I am using NancyFx(No EF) + Breeze + AngularJS for my web project and generating breeze metadata offline(using EF methods at development) and then using it in js file.
I also encountered similar situation where I want to get all Enum values to bind dropdowns and to display EnumName corresponding to EnumValue(Id). I searched over net but there was not much as per my scenario.
So I have written raw JS methods
1. To extract all enums and their values(Id & Name) in a JS dictionary(associated array) from metadata.
var enumDictionary = {};
JSON.parse(window.app.metadata).schema.enumType.forEach(function (enumType) {
var newEnumValues = [];
enumType.member.forEach(function (enumValue) {
var newEnumValue = { id: enumValue.value, name: enumValue.name };
newEnumValues.push(newEnumValue);
});
enumDictionary[enumType.name] = newEnumValues;
});
I created a method to get all enum values for a specific enum. This will be used for binding a dropdown.
function GetEnumDictionary(enumName) {
return enumDictionary[enumName];
}
Another method I created to get specific Enum name on basis of value.
function GetEnumDictionaryValue(enumName, enumValueId) {
var result = null;
enumDictionary[enumName].some(function (enumValue) {
if (enumValue.id == enumValueId) {
result = enumValue.name;
return;
}
});
return result;
}

breezejs entity query with select returning undefined

With the code below I am having an issue where not all the columns are return data in the data.results array. For example if col4 is null in the database for row 1 then data.results[0] does not contain an element for col4, but row 2 has a value then data.results[1] will contain the value for col4. I would like each return item in the array to contain all items with the database value or null. If null can't be returned then an empty string would do.
var query = new breeze.EntityQuery()
.from('mytable')
.where('col1', 'substringof', '2')
.select('col1,col2,col3,col4')
.orderBy('col1')
.take(200);
return _manager
.executeQuery(query)
.then(function (data) {
return data.results;
})
.fail(queryFailed);
}
By default breeze does not serialize null values in its JSON results. This was a deliberate choice to reduce the breeze payload over the wire. And.. this is not typically an issue with queries that return "entities". i.e. data for which breeze metadata exists. Because the properties are already defined on such entities.
But if you are returning an anonymous result as you are, then this can be a problem. You can obviously work around it because you know the properties that you are requesting and can update them after the query if they are not in the result.
But you can also change breeze's default configuration to accommodate this via the "BreezeConfig" class.
BreezeConfig enables customization of components supporting Breeze-related operations in the Web API. BreezeConfig defines default behaviors; you can substitute your own behaviors by deriving from it and overriding its virtual methods. Breeze.NET will discover your subclass among the assemblies referenced by your project and use it instead of BreezeConfig.
To use BreezeConfig to configure the Json.Net serializer with specific settings. You can replace those settings by writing a subclass of BreezeConfig that overrides the 'CreateJsonSerializerSettings' method as shown in this example:
public class CustomBreezeConfig : Breeze.WebApi.BreezeConfig {
protected override JsonSerializerSettings CreateJsonSerializerSettings() {
var baseSettings = base.CreateJsonSerializerSettings();
baseSettings.NullValueHandling = NullValueHandling.Include;
return baseSettings;
}
Hope this helps.

Resources