How to extend Breeze MetaData for Unmapped Entity Property without KO - c#

There are some posts about this, but nothing specific that works in my project.
I read the docs on Breeze about Extending Entities, but they use knockout and I am using Angular.
I have defined a custom property on the server and it is being passed down in my JSON.
However, Breeze js ingnores it because there is no meta data for it.
I need to define the meta data on the client so that Breeze can read the property.
Here is what I have the client so far, but it does not work. By not working... I mean when I call it with {{item.MyProp}} nothing ever shows up on the screen. However, all the other properties from the actual meta data show up just fine.
configureBreeze();
var serviceName = 'api/Entity';
var manager = new breeze.EntityManager(serviceName);
manager.enableSaveQueuing(true);
var store = manager.metadataStore;
addMyPropType(store);
function addMyPropType(store) {
store.registerEntityTypeCtor("Merchant", MyProp);
}
// custom Merchant constructor
var MyProp= function () {
//'MyProp' is a server-side calculated property of the Merchant class
// This unmapped property will be empty for new entities
// but will be set for existing entities during query materialization
this.MyProp= "test";
};
var dataservice = {
store: store,
List: List,
Create: Create,
ListDetail: ListDetail,
Save: Save
};
return dataservice;
I have ready the NODB sample, but I do have a DB and it also uses KO.
UPDATE:
Ok. So I found something that partially works. The default value is now getting displayed on the view. However, the value from the JSON is not being filled in. It always uses the default value.
This is what I have now:
var Merchant = function () {
this.MyProp = "5";
};
store.registerEntityTypeCtor("Merchant", Merchant);
What needs to happen for MyProp to be filled by the actual value from the JSON?

If camelCasing is not your case as PWKad suggested then check breeze documentation for the Breeze Angular SPA template here http://www.breezejs.com/samples/breezeangular-template . There is a section in that link called "Extending entity definitions on the client" .
If I understand correctly you have a "Merchant" object with a "MyProp" calculated property. Try this
store.registerEntityTypeCtor("Merchant",Merchant, merchantInitializer);
function Merchant(){
this.MyProp="";
}

Well, it seems this is a bug in Breeze. You actually have to edit the breeze.js file to get it working. I never would have thought it was a bug.
I found the answer here:
Breeze Extended Entity Property Only Loads on Second Query
UPDATE:
Today I updated to the latest version of breeze.js and the bug does not exist anymore. So this was basically a lot of pain for no reason. Thanks everyone for the help. If you cannot update for some reason use the link above.

Related

C# Google SDK Update or Patch User

Having problem using update User with the google Admin SDK for C#.
https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/update
This method supports patch semantics, meaning you only need to include the fields you wish to update. Fields that are not present in the request will be preserved, and fields set to null will be cleared.
This differs from the Patch as patch won't clear fields that are null but only update fields that have a value.
Problem is that I have to pass a full Google.Apis.Admin.Directory.directory_v1.Data.User class to the function which will contain null of even properties i do not want to clear.
example:
public User UpdateUser(Google.Apis.Admin.Directory.directory_v1.Data.User gUser)
{
UsersResource.UpdateRequest userUpdateRequest = _service.Users.Update(gUser, gUser.Id);
User updatedUser = userUpdateRequest.Execute();
return updatedUser;
}
Is there any way of modifying the Body in UpdateRequest before executing it?
Edit:
The UpdateRequest has a ModifyRequest Property that looks like this
I just have no Idea how to use it, any ideas?
public Action<HttpRequestMessage> ModifyRequest { get; set; }
As far as updating things to the concept of Null that is not something that can be done with PATCH. I recommend setting it to an empty string.
You should also not be sending the full user object if thats what you are currently doing. I am going to assume that you have done a users.list to find the user you want to update and change something in that user, say the name. Then you have simply submited the full user object to your method
UpdateUser(Google.Apis.Admin.Directory.directory_v1.Data.User gUser)
This wont work as some of the fields you have sent as part of the update/patch are not actually writeable.
What you should do instead would be to create a new user object change what ever it is you want
public User MakeUserAdmin(Google.Apis.Admin.Directory.directory_v1.Data.User gUserId)
{
var updateFields= new Google.Apis.Admin.Directory.directory_v1.Data.User();
change.IsAdmin = true;
change.Addresses = ""; // will set it to empty yes not null but the best you can do with this api.
UsersResource.UpdateRequest userUpdateRequest = _service.Users.Update(updateFields, gUserId);
User updatedUser = userUpdateRequest.Execute();
return updatedUser;
}
Notice how you just need to create a new object and update only the fields you need then send that.
Dont try to update every field, just update the ones that you know have changed. Dont include the id in the object that is not writeable either.

MongoDocument Object only gets partially populated when querying

I have a collection of users that I'm trying to read from the database, but for some reason some strange behavior takes place that I can't really figure out. Hopefully, somebody can suggest or help me find the root of this problem.
So basically, what happens is that whenever I call this code in my HomeController.cs:
var users = await _database.GetCollection<User>("ApplicationUsers").FindAsync(_ => true);
var userList = users.ToList();
it only populates userList partially, meaning only the ID and ConcurrencyStamp properties get filled, but the other properties always end up being null (as seen in: https://i.imgur.com/RTF8ljL.png)
But whenever I add this line right after the database connection initialization in the Startup.cs:
database.GetCollection<User>("ApplicationUsers");
Then suddenly userList does get populated with all the other information (as seen in https://i.imgur.com/f5IV7fh.png)
So in order for it to work, I have to get the collection right after the connection gets initialized which I'm not really fond of, because I don't have to do this for other collections. So my mongo connection code would have to look like this in the Startup.cs:
var mongoUrl = new MongoUrl(config.GetSection("DatabaseSettings:ConnectionString").Value);
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
mongoClientSettings.ClusterConfigurator = cb => ConfigureCluster(cb);
var client = new MongoClient(mongoClientSettings);
var database = client.GetDatabase(config.GetSection("DatabaseSettings:DatabaseName").Value);
database.GetCollection<User>("ApplicationUsers"); // TODO: This is needed just to let Mongo Driver know to which class to deserialize this collection
services.AddSingleton<IMongoDatabase>(database);
var pack = new ConventionPack()
{
new CamelCaseElementNameConvention(),
new IgnoreExtraElementsConvention(true),
new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)
};
ConventionRegistry.Register("DatabaseConventions", pack, t => true);
I'm guessing something happens between the execution of Startup.cs and HomeController.cs that causes the deserialization to mess up?
Update:
The same behavior seems to happen on a clean project, the only nuget packages I installed are the official mongodb driver and AspNetCore.Identity.Mongo by Matteo Fabbri. This strange deserialize behavior does not happen when I use getCollection for other classes. The problem lies with ApplicationUser which extends from MongoUser (a class made available by the AspNetCore.Identity.Mongo library)
Update 2:
Turns out that MongoUser class from the AspNetCore.Identity.Mongo library is allergic to the ConventionPack that was registered. I tested this by getting the collection before and after the registration of the database conventions. Now finding a proper solution for this.
Update 3:
Also I found out that documents are saved with properties named in Upper Camel Case, which could be the cause for mongodb driver's confusion. It seems that the conventions set to save them in CamelCase is being ignored for this particular class (ApplicationUser).
I managed to solve the problem. The issue was the order of code execution. The convention pack was registered after MongoIdentity and the database was initialized. During the initialization of MongoIdentity and the Database, the ApplicationUser was configured to not have the CamelCaseElementNameConvention in the pack, which led to the class being saved in UpperCamelCase and therefore cause confusion when retrieving the collection when the registered ConventionPack was active.
For whoever struggles with strange behavior where you may think your code should work, remember that the order of code execution is very important and could very well be the cause of the behavior. Good luck to y'all!

Add field to a Mongo document using a string for its name

I'd like to create a new field on an existing document. I'm using the following line to get all the documents out of the database into POCOs:
var collection = _database.GetCollection<Address>(collectionName);
I then make some changes to each and save them back into the database using this line:
collection.ReplaceOne(
filter: new BsonDocument("_id", a.id),
options: new UpdateOptions { IsUpsert = true },
replacement: a);
That all works nicely.
To add a field, I've tried the following:
a.ToBsonDocument().Add("RainfallMM", rainfallMM);
But it doesn't make it to the database. Is there a trick to it?
I asked you in comment did you add new property to address model and you said you didn't and you want to add it dynamically. Trick here is you initialize your collection as collection of addresses and mongo ignores all properties that are not part of address model by default.
If you want to add it dynamically you need to change how you start your collection to:
var collection = _database.GetCollection<BsonDocument>("addresses");
Now your collection is not tied to address model and you can work with it as you wish. Everything is BSON document now!
For example you can do this:
var inserted = MongoCollection.Find(x => true).FirstOrDefault();
inserted.Add(new BsonElement("RainfallMM", rainfallMM);
MongoCollection.ReplaceOne(new BsonDocument("_id", inserted["_id"]), inserted);
PS there are some other workarounds and if this one you don't like I can show you the rest =)
Hope it helps! Cheers!
As #Fidel asked me to I will try to briefly summarize other solutions. The problem in accepted answer is that while it works it loses it's connection to Address model and OP is stuck with working with BSON documents.
IMO working with plain BSON documents is pain.
If he ever wishes to change back to initializing collection as collection of Addresses and tries to get anything from db he will encounter an error saying something like:
Missing serialization information for rainfallMM
He can fix that by including tag above his Address class like this:
[BsonIgnoreExtraElements]
public class Address
{
...fluff and stuff...
}
Problem now is if he is not careful he can lose all his info in dynamically added properties.
Other problem is if he adds another property dynamically. Now he has to remember there are 2 properties which are not in model and the hell breaks loose.
Weather he likes it or not, to make his life easier he will probably have to modify Address model. There are 2 approaches and their official documentation is great (I think), so I will just link it here:
http://mongodb.github.io/mongo-csharp-driver/2.4/examples/mixing_static_and_dynamic/
IF you ask me which one is better I will honestly tell you it depends on you. From documentation you will see that if you use an extra BSON document property you don't have to worry about naming your extra properties.
That would be all I can think of now!
Hope it helps you!
try with
a.Add({"RainfallMM", rainfallMM});

Can't query/order on built-in rally fields "could not read all instances of class com.f4tech.slm.domain.Artifact"

I'm using v2.0 of the API via the C# dll. But this problem also happens when I pass a Query String to the v2.0 API via https://rally1.rallydev.com/slm/doc/webservice/
I'm querying at the Artifact level because I need both Defects and Stories. I tried to see what kind of query string the Rally front end is using, and it passes custom fields and built-in fields to the artifact query. I am doing the same thing, but am not finding any luck getting it to work.
I need to be able to filter out the released items from my query. Furthermore, I also need to sort by the custom c_ReleaseType field as well as the built-in DragAndDropRank field. I'm guessing this is a problem because those built-in fields are not actually on the Artifact object, but why would the custom fields work? They're not on the Artifact object either. It might just be a problem I'm not able to guess at hidden in the API. If I can query these objects based on custom fields, I would expect the ability would exist to query them by built-in fields as well, even if those fields don't exist on the Ancestor object.
For the sake of the example, I am leaving out a bunch of the setup code... and only leaving in the code that causes the issues.
var request = new Request("Artifact");
request.Order = "DragAndDropRank";
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I comment the Order by DragAndDropRank line, it works.
var request = new Request("Artifact");
request.Query = (new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue").
And(new Query("Release", Query.Operator.Equals, "null")));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the Release part out of the query, it works.
var request = new Request("Artifact");
request.Query = (((new Query("TypeDefOid", Query.Operator.Equals, "someID").
And(new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue"))).
And(new Query("DirectChildrenCount", Query.Operator.Equals, "0"))));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the DirectChildrenCount part out of the query, it works.
Here's an example of the problem demonstrated by an API call.
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState%20%3D%20%22Backlog%22)&order=DragAndDropRank&start=1&pagesize=20
When I remove the Order by DragAndDropRank querystring, it works.
I think most of your trouble is due to the fact that in order to use the Artifact endpoint you need to specify a types parameter so it knows which artifact sub classes to include.
Simply adding that to your example WSAPI query above causes it to return successfully:
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState = "Backlog")&order=DragAndDropRank&start=1&pagesize=20&types=hierarchicalrequirement,defect
However I'm not tally sure if the C# API allows you to encode additional custom parameters onto the request...
Your question already contains the answer.
UserStory (HierarchicalRequirement in WS API) and Defect inherit some of their fields from Artifact, e.g. FormattedID, Name, Description, LastUpdateDate, etc. You may use those fields in the context of Artifact type.
The fields that you are trying to access on Artifact object do not exist on it. They exist on a child level, e.g. DragAndDropRank, Release, Iteration. It is not possible to use those fields in the context of Artifact type.
Parent objects don't have access to attributes specific to child object.
Artifact is an abstract type.
If you need to filter by Release, you need to make two separate requests - one for stories, the other for defects.

Updating a property using the Content Service api in Umbraco 6.x

I've created a custom user control for the back-end of my Umbraco site that allows administrators to quickly update certain fields on nodes without having to navigate through the content tree.
So far my code is working as expected: I can update simple true/false properties without a problem. However now I'm trying to update a property that's of a custom data type and I'm running into difficulties.
The data type itself is just a simple drop down that lists a series of availability statuses ie. Available, Unavailable, Sold and Reserved. The datatype is storing the text values.
Here's the code I have that allows me to update my true/false properties:
public void ChangeInteractiveStatus(string nodeId, bool chkValue)
{
var cs = ApplicationContext.Current.Services.ContentService;
var apartment = cs.GetById(Convert.ToInt32(nodeId));
apartment.SetValue("displayOnInteractive", chkValue);
cs.SaveAndPublish(apartment);
}
This works absolutely fine as the data type of this property is a regular true/false data type.
Here's the code I'm using to change the value of my custom dropdownlist data type:
public void ChangeAvailabilityStatus(string nodeId, string status)
{
var cs = ApplicationContext.Current.Services.ContentService;
var apartment = cs.GetById(Convert.ToInt32(nodeId));
apartment.SetValue("status", status);
cs.SaveAndPublish(apartment);
}
As you can see there's very little difference and yet this code isn't working.
In order to check what was happening when I was updating the properties with the above code, I checked the umbraco.config file only to find that the property in question was displaying as follows:
<status><![CDATA[]]></status>
However when I change the value in the content tree (without using my admin control) the value gets saved properly as:
<status><![CDATA[Sold]]></status>
So for whatever reason, when I try to update the value it's being rejected and I can't work out why.
FYI I tried entering the value as:
"<![CDATA[" + status + "]]>"
Yet that made no difference.
Does anyone know how I can fix this? How can I get the property to update correctly?
Thanks
Okay I've figured out what the problem was. It seems the values were being stored as name-value pairs, so the actual value getting stored in the database was an integer. Once I updated the code to insert the integer id it all worked as expected! Hooray.

Categories

Resources