How do I update a MongoDB document but exclude a specific field? - c#

I am creating a web API. I need something like this:
When I updating a document at mongodb, I do not want to update a field (createdAt). I know that I can get a old value of that field and manuelly and then put it updated object but it requires one more unnecessarry request to db. I do not want this. My method is here:
public async Task<bool> UpdateAsync(Customer updatedCustomer)
{
var result = await _mongoService.Customers.ReplaceOneAsync(c => c.Id == updatedCustomer.Id, updatedCustomer);
return result.IsModifiedCountAvailable && result.ModifiedCount>0;
}
Is there any way to exclude one property of my Customer class (createdAt) and left it same everytime. BTW please do not recomend that set all properties update one by one by using "Set" method. Thank you.

I'm not sure if there is a way other than to set the properties one by one, but researching the following may be helpful or suggestive of something new.
In Mongodb you can use some decoration to do like [BsonIgnore] but it will ignore it every time
One alternative would be to load the document you wish to modify, followed by calling BsonDocument.Merge with overwriteExistingElements set to true in order to merge your changes.

Related

acumatica c# / Add SOPackageDetailEx

I have this error when I try to add a line of package
Error : Another process has added the "SOPackagedetail" record. Your changes will be lost.
error
My c# code is this :
protected virtual void creationColis()
{
SOShipment ship=Base.CurrentDocument.Select();
SOPackageDetailEx colis = new SOPackageDetailEx();
colis.BoxID="COLIS";
colis.PackageType="M";
colis.ShipmentNbr=ship.ShipmentNbr;
SOShipmentEntry graph = PXGraph.CreateInstance<SOShipmentEntry>();
graph.Packages.Insert(colis); //insertion de l'enregistrement
graph.Packages.Update(colis);
graph.Actions.PressSave();
graph.Clear();
}
Do you know what I must to change please ?
Thanks so much
Xavier
Your question needs more context. For starters, where does your code reside? Given that you reference Base.CurrentDocument.Select, I'm going to assume you are extending SOShipmentEntry to add your code.
In this case, you would just use the Base.Packages view rather than initializing your own instance of SOShipmentEntry where your example goes into trying to use graph.Packages. Regardless, there are 2 parts here that need to be addressed.
Packages is not the primary view of SOShipmentEntry. When you create an instance of a graph, you must tell the graph what record is needed in the primary view. In your example where you create a new instance of a graph, you might do something like this:
graph.Document.Current = graph.Document.Search<SOShipment.shipmentNbr>(myShipmentNbr);
If you are working on a graph extension of SOShipmentEntry, then you probably don't need to create a new instance of the graph. Just make sure graph.Document.Current isn't null before you add your package record - see bullet 2.
Once you have a shipment selected, you can then insert your package information. However, the way you have done it here effectively is trying to add a random package to a null shipment (by the structure of the views) but forcing the record to attach to the right shipment by sheer brute force. The views don't like to work that way.
A better way to add your package once you have a current shipment (Document) is like this:
// Find the current shipment (from the primary view Document)
SOShipment ship = Base.Document.Current();
if(ship?.ShipmentNbr != null) {
// Insert a record into the Packages view of the current shipment and return the record into colis
SOPackageDetailEx colis = Base.Packages.Insert(colis);
// Set the custom values
colis.BoxID="COLIS";
colis.PackageType="M";
// Update the Packages cache with the modified fields
Base.Packages.Update(colis);
// If more fields need to be updated after those changes were applied, instead do this...
colis = Base.Packages.Update(colis);
colis.FieldA = ValueA;
colis.FieldB = ValueB;
Base.Packages.Update(colis);
// If a save is needed, now is the time
Base.Save.Press();
}
Notice that I didn't assign ShipmentNbr. That is because the DAC has that field defined to pull the ShipmentNbr from SOShipment through these 2 attributes.
[PXParent(typeof(FK.Shipment))]
[PXDBDefault(typeof(SOShipment.shipmentNbr))]
This means that when the record is created, Acumatica should lookup the parent SOShipment record via the Key and do a DBDefault on the field to assign it to the SOShipment.ShipmentNbr value (from the parent). Important side note: PXDefault and PXDBDefault are NOT interchangeable. We use PXDefault a lot, but off the top of my head I can't think of a case of PXDBDefault outside of defaulting from a database value like this specific usage.

how to search other field than id

I'm following and implementing a side project from:
https://learn.microsoft.com/es-mx/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.0&tabs=visual-studio
But in the part where [HttpGet("{id}")] is invoked, it works only with the id field, but I want to retrieve a JSON stored in the DBmemory, with other field instead of id; in this case I want to manage data by field TAG.
How can I accomplish this?
I've try to change all the id parts to TAG, which is the field I'm looking for, but when I do this, the post method breaks up.
// GET: api/Maquinas/5
[HttpGet("{id}")]
public async Task<ActionResult<Maquina>> GetMaquina(string id)
{
// HERE. i need to find data with the field of "TAG" not "id"
var maquina = await _context.Maquinas.FindAsync(id);
if (maquina == null)
{
return NotFound();
}
return maquina;
}
Don't get stuck on the fact that is called id. You could make use of this endpoint and instead of passing the value of id to pass the value of tag, api/Maquinas/tagvalue.
Later on you should use this value in the call you make to retrieve the entity you are looking for.
_context.Maquinas.FirstOrDefaultAsync(maquina => maquina.Tag == id);
I have assumed here that the property on which you want to filter is called Tag and it's type is string. It is quite probable, at least the name of the property to not be this one. So you have to change the above code correspondingly.
This will fix your problem, but you should not consider this a best practice. The semantics of your API would be broken. The very reason, I shared the above, is to show you that the name of the parameter id is irrelevant with that you pass. There isn't any check that would halt you for passing there "anything". The reason I wrote that the semantics of your API would be broken is that since this is going to be a REST api, someone would expect an endpoint like the following one:
api/Maquinas/1
for getting the entity with id 1.

c# MVC 5 User Store custom findby

the default provided FindByIdAsync, FindById etc work as expected.
How would I go about implementing a FindBySomeOtherField?
at a minimum i'd like to be able to UserManager.FindBy any field off the main AspNetUsers table. Such as UserManager.FindByPhoneNumber
Ideally, i'd be able to UserManager.FindByCustomField
I do NOT want to implement a completely new user store.
I guess worst case scenario I could go find and copy the entire user store for MVC 5 as is and add it to my project and then implement it, however, i'd rather just add a class that includes the function and be done since I rather like the user store as is and just want to extend it's functionality.
Not sure if I'm understanding correctly, but the following should work?
IEnumerable<[Object]> results = [ObjectCollection].Where(u => u.[Field] == [Value])
OR
[Object] result = [ObjectCollection].SingleOrDefault(u => u.[Field] == [Value])
If you want it for a specific field, you could extract it into a method;
public [Object] FindBy[Field]([FieldType] value)
{
return [ObjectCollection].SingleOrDefault(u => u.[Field] == [Value])
}

Orchard CMS: Adding default data to fields and then querying them

I have added a LinkField called Website to a content type using a part with the same name as the content type.
ContentDefinitionManager.AlterTypeDefinition("MyContentType", a => a
.WithPart("CommonPart")
.WithPart("MyContentType")
.Creatable());
ContentDefinitionManager.AlterPartDefinition("MyContentType", cft => cft
.WithField("Website", a => a.OfType("LinkField").WithDisplayName("Website")
.WithSetting("FieldIndexing.Included", "True"))
.Attachable());
I then create some default content items during the migration.
I'm creating the item before adding the field data because I have had problems with fields not being updated when their values are set before the item is created. (Feel free to shine some light on that, but that isn't my question though)
var myItem = _orchardServices.ContentManager.New("MyContentType");
_orchardServices.ContentManager.Create(myItem);
var websitePart = myItem.Parts.FirstOrDefault(x => x.Fields.Any(y => y.Name == "Website"));
var websiteLinkField = websitePart .Fields.FirstOrDefault(x => x.Name == "Website") as LinkField;
websiteLinkField.Value = "http://www.google.com";
websiteLinkField.Text = "Link to google";
_orchardServices.ContentManager.Publish(myItem);
I realize there are more dynamic ways to access the field, but this seems to work too.
The data shows up when I view the items, but then I move on to making a Query.
I use the UI to build a simple query looking for the word "google" in the text of the LinkField, then I hit preview.
No results are found.
I then open up one of the items created from the migration and simply hit the "Save" button.
Then I try the preview again and the item I saved now shows up.
So as far as I can tell something is happening when I save a content item that I'm not doing from the migration. But I have been stepping through the code going over all angles, and I just can't find it.
I suspect maybe some handler is supposed to get triggered in order to create the FieldIndex'es ?
(I know how to trigger an update for the Lucene index, but as one would expect it does not affect querying fields using the Projections module and I'm really lost at this point.)
By now I'm just stabbing blindly in the dark.
Would really appreciate any help I can get, even if it's just something pointing me back in the right direction. Thank you.
You should change
_orchardServices.ContentManager.Create(myItem);
to
_orchardServices.ContentManager.Create(myItem, Orchard.ContentManagement.VersionOptions.Draft);
For understanding look at CreatePOST method of Orchard.Core.Contents.Controllers.AdminController class and Publish method of Orchard.ContentManagement.DefaultContentManager class
In your case when you call a Create(myItem) then created published content item and all handlers are invoked normally (but has not yet set up a desired data). When you call Publish(myItem) nothing happens (no handlers are invoked) because your content is already published.
I've raised this as a bug, vote for it if you think it needs fixed.
#Alexander Petryakov is correct in his description of what is happening and his work around is probably the correct approach, however the behaviour doesn't make sense, which is why I have raised the bug. The code in your question manages to create an inconsistency between the content view of the data, stored in the Orchard_Framework_ContentItemVersionRecord table and the Projections view of the data stored in the Orchard_Projections_StringFieldIndexRecord table. Essentially, the Orchard_Projections_StringFieldIndexRecord contains null because it hasn't processed the publish event after you updated the field.
The code you have essentially does the following things:
Create a content item + publish it's creation
Update one of the content items fields this update doesn't change the state of the content
Try to publish the content item which doesn't do anything because it thinks it is already published.
To me, if you update a field on the content item, then the state of the item you are working on should no longer be published (it's changed since you published it). The Fields provide hooks that allow you to be notified when they are updated, so an alternate way of solving the problem would be to create a class that implements the interface IFieldStorageEvents that updates the published state of the content when a field is updated.
public class FieldUpdateEventHandler : IFieldStorageEvents {
public void SetCalled(FieldStorageEventContext context) {
context.Content.ContentItem.VersionRecord.Published = false;
}
}
This would allow your original code to run as it was written.

Get all writeable properties of an ADLDS-Class

I'm developing an application which can deal with a MS-ADLDS-Service.
Currently it is possible to create Directory-Entries and assign values to some properties.
Not a realy exciting task until this:
Im my application it's possible (it should be) to configure which properties of a class (for instance: the CN=Person class) should be assigned with values which are evaluated at runtime in my application.
Long story short:
I want to retrieve all (writeable) properties of a class. Without creating and saving a new CN=Person-Object before.
Currently i use my schemaBinding to get the Directory-classSchema-Entry of the Person-Class (CN=Person) from where i read some property-values (like "AllowedAttributesEffective", "mayContain", "AllowedAttributes") - i get the most properties by this way - but some Properties are missing! For instance the "telephoneNumber"-Property (attributeSchema: CN=Telephone-Number)
Does anybody know how to get these properties of a class? ADSI-Edit does this: when i create a new object with adsi-edit i can assign values to all possible properties before committing the new entry.
thanks a lot for any hint!
(.net code is welcome)
I have found the solution for my task!
Some of these properties are "calculated" and not persistent at the directoryentry.
So its meant to call the RefreshCache() Method and pass the needed property names as an string array.
directoryEntry.RefreshCache(new string[] { "allowedAttributesEffective",
"allowedAttributes",
"systemMayContain",
"systemMustContain" });
After that call, the properties have values....
if (directoryEntry.Properties["systemMayContain"]).Value != null)
{
/// Success
}

Categories