OrchardCMS Infoset and partrecord getting out of sync - orchardcms

I'm finding that my partrecord (backing record) and infoset record are getting out sync, when using ContentItemVersionRecord.
The site has been developed using dynamic forms, and custom part records, such as...
public class ParticipantPartRecord : ContentPartVersionRecord
{
public virtual string ParticipantId { get; set; }
}
public class ParticipantPart : ContentPart<ParticipantPartRecord>, ITitleAspect
{
public string ParticipantId {
get { return Retrieve(r => r.ParticipantId); }
set { Store(s => s.ParticipantId, value); } }
}
When updating (I'm using a fork of the dynamic forms module which allows edits - see issue 6163) a record through a DynamicForm the FormController creates a draft of the ContentItem, updates the bindings, and publishes the ContentItem, which updates the infoset record in ContentItemVersionRecord and (I assume) triggers an update of the backing record.
There are some fields which I update via workflows (as they are generated programatically, rather than user set on the form). When I update these records (on the previously published draft, so latest version), the infoset updates, however the backing record does not seem to.
As most of the data retrieval is using _contentManager.Get(id) I hadn't noticed the issue, however when using _contentManager.Query.Where() on the programatically updated records, I discovered that the backing record no longer matched the InfoSet record in Orchard_Framework_ContentItemVersionRecord
When updating the records programatically in the workflows I'm using code like
var participant = workflowContext.Content.ContentItem.As<ParticipantPart>();
participant.ParticipantId = GenerateParticipantId();
This updates the InfoSet record in ContentItemVersionRecord, but the backing record does not appear to be updating.
I haven't used VersionedRecords before, and I wonder if I need to be creating a draft, and publishing in order to force the backing record to update, or if there is something in the Handler that needs to be set to trigger the update to the backing record.

Related

Defaulting user-defined fields for records created before the customization

I have a DAC extension that has a default attribute set for it(see definition below). I have noticed that for any records that existed prior to the DAC extensions existence will still hold a null value. Is there a conventional way that we can get all the records initialized?
If not I will likely perform a check within an event handler before its used.
Thanks in advance!
public abstract class usrCustOptInOut : IBqlField
{
}
protected string _UsrCustOptInOut;
[PXDBString(1, IsUnicode = true)]
[PXDefault(OptInOut.Default, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Click To Pay Opt In/Out")]
[PXStringList(
new[]
{
OptInOut.OptOut,
OptInOut.OptIn,
OptInOut.Default
},
new[]
{
"Customer Opt Out",
"Customer Opt In",
"Default"
})]
public virtual string UsrCustOptInOut
{
get
{
return _UsrCustOptInOut;
}
set
{
_UsrCustOptInOut = value;
}
}
You will need to run an update manually. Existing records will not be updated automatically when adding new fields and records exist.
I see two options:
Add a a customization plugin to your project (preferred)
Add a custom SQL script to your project
Option 1
You can use a customization plugin to make sure all null values have a default. The customization plugin will run at the end of each publish within the site.
You can create a code file in your customization project to this:
The UpdateDatabase() method in a CustomizationPlugin runs after the customization was published and the website was restarted (the Usr field will be applied).
Then you can use PXDatabase.Update to run the update without needing a graph or any validation/events to run. You just need a bulk SQL update and PXDatabase.Update will do the job. The example below will only set a default if there is a null value. The same concept can be applied to any table/field added not just user fields. For example if you have a custom table and you need to add a new field and have existing records contain a default.
A down side (when compared to Option 2) is PXDatabase.Update will run for the current tenant (company) as it appends the current CompanyID to the SQL statement (which is a good thing). If you have multiple tenants (companies) you will need to run the publish for multiple tenants. Alternativly you can write your code so that it will loop each company and run your statement using PXLoginScope (see references).
The end result using a Customization Plugin would look something like this:
public class UsrFieldDefaults : CustomizationPlugin
{
//This method executed after customization was published and website was restarted.
public override void UpdateDatabase()
{
PXDatabase.Update<MyDac>(
new PXDataFieldAssign<MyDacExt.usrCustOptInOut>(PXDbType.NChar, 1, OptInOut.Default),
new PXDataFieldRestrict<MyDacExt.usrCustOptInOut>(PXDbType.NChar, 1, null, PXComp.ISNULL)
);
}
}
Option 2
You can write a custom SQL script to do the same and put it in your customization project. Keep in mind which DBMS you are publishing to (MSSQL/MYSQL). There is also a way to use a shared SQL syntax so it should work for any DBMS. Also be aware that you would most likely run the script for all tenants (companies) which would include snapshots.
Additional References:
Custom Processes During Publication of a Customization
To Add a Customization Plug-In to a Project
Save data to different company
To Publish a Customization for a Multitenant Site
Creating a Custom SQL Script
Writing Custom SQL Scripts for Interpretation

Versioning for ContentPart's without a record

Is it possible to create draftable versions of content parts that have no record.
For example
public MyContentPart : ContentPart
{
...
}
When I create a draftable version of MyContentPart it doesn't behave the same as a part that inherits from ContentPart<MyContentPartVersionRecord> would. E.g. change some values of it's properties and still see the original values in the last published version.

Orchard cms tracking record repository like tracking content item in Audit Trail

I'm using Orchard's Audit Trail module to track change content items.
But, in my project, I has some objects that only have record model (don't have part model), I query and save them by using Repository.Fetch()
In Audit Trail module, it override Updating(), Updated() method to track change a content item.
I want to know if there are something like that for record Repository, because I also want to track objects that only use record model.
Ex:
I have these models : InvoicePart, InvoicePartRecord, DetailRecord, ProductPart, ProductPartRecord
An Invoice has many Details.
Detail stores Invoice_Id & Product_Id, salesman can add more Details into Invoice, or delete it from Invoice, or change Product_Id of a Detail.
Orchard Audit Trail can only tracking change for Invoice & Product, but not for Detail because it is not a content item, so that I can not know when a Detail was created, modified or deleted.
To do this, you must add new repository implementation inherited from the original one, like this:
public class MyRepository<T> : Repository<T>, IRepository<T> where T : class
here, you can trigger your events whenever you want to track changes.
Then, to suppress the original implementation you must add new Autofac Module class:
public class DataModule : Module {
protected override void Load(ContainerBuilder builder) {
builder.RegisterGeneric(typeof(MyRepository<>)).As(typeof(IRepository<>)).InstancePerDependency();
}
}
after that you can handle these events and audit them, you will find a good sample on this in Orchard.AuditTrail module with ContentDefinitionProviders.
Note: if you want to specify the records which have auditing enabled (this useful if you don't want to audit all the changes on all records in your system), you can add a new class attribute:
[AttributeUsage(AttributeTargets.Class)]
public sealed class AuditTrailEnabledAttribute : Attribute {
}
then use this attribute to check if the record class has it or not, and do your auditing based on it.

Should I add item using repository pattern or a create event if I am using domain events?

I am trying to understand the Domain Event pattern illustrated by Udi Dahan with regard to adding new domain entities in a certain situation.
Now normally with entities I would create them and then add them via repository. I assume I would still do this?
My example is we normally add assets to the system. Like this:
var asset= new Asset();
/*bunch of prop setting*/
_assetRepository.Add(asset);
However asset creation is an event that we want to follow certain processes as a result of. Therefore it was suggested by developer we no longer need to do this as it could be handled by domain event:
var asset= new Asset();
/*bunch of prop setting*/
asset.Create(location);
Now a create method would raise an event and be handled by a create event handler that basically just inserts it into the repo and does some other stuff email the warehouse manager of the create location etc.
However having a create event on the asset looks pretty active record to me. However in the domain people talk about new assets being created. So we were not sure.
Thoughts?
The created domain event should be raised in the constructor of the Asset class because that is when that particular entity is created. In your current implementation, this would be erroneous because the Asset entity provides a parameterless constructor. Instead, create a constructor which has all required properties as parameters thereby preventing creation of an Asset entity in an inconsistent state. It could look like this:
public class Asset
{
public Asset(string prop1, decimal prop2)
{
this.Prop1 = prop1;
this.Prop2 = prop2;
DomainEvents.Raise(new AssetCreated(prop1, prop2));
}
public string Id { get; private set; }
public string Prop1 { get; private set; }
public decimal Prop2 { get; private set; }
}
You still have to persist the entity using the repository after creating it. This can be problematic because the handlers for the AssetCreated cannot reference its ID since it is not yet assigned when they are notified. If using event sourcing, then the creation event would be explicitly stored in the underlying event store.
I've been struggling for this problem for quite a long time. But no good solution. I think,
A domain event shouldn't be published or handled before the aggregate it belongs to being successfully persisted
It's not the application layer's responsibility to publish any domain events
So far, I think the best approach is to take advantage of AOP. We can "fire" events in the aggregate, but instead of dispatching them instantly, we keep it in a queue, and really dispatch it after the corresponding transaction successes. We can define a custom #Transactional interceptor to achieve this, thus keeping the app service from knowning any concept of "event publishing".

Domain Driven Development: Detecting changes (.NET)

I've just started with Domain Driven Design and trying to apply it for my current project.
I've started with a pure domain model and now stuck with my Data Access layer. I have a completely home made data access layer therefore no any of well known ORM tools can be applied here.
I cannot figure out how to deal with updates. Let's say I have the following Objects:
public class Document : Entity
{
public IPropertiesCollection Properties { get; set; }
public IContents Contents { get; set; }
}
public class PostalDocumentsPackage : Entity
{
public String Name { get; set; }
public DateTime DeliverDate { get; set; }
public ICollection<Document> Documents { get; set; }
}
I have corresponding repositories IDocumentsRepository and IPostalDocumentPackagesRepository for retrieving objects (for now).
The problem I deal with now is to situation when i want to add a new document do Documents collection of PostalDocumentsPackage. Basically I see two possible cases here:
1) Implement the collection that track changes in original collection and holds lists of items that were updated\removed.
2) Implement separate methods in repository for adding documents to the package.
I wonder are these approaches is ok or can cause problems in future? or there is another alternatives?
Typically change tracking would be handled by an ORM such as NHibernate. In your case you may be able to do the following:
Select new documents based on the value of the identity property.
Issue a SQL delete statement before re-inserting into the table.
A problem with either approach is that the documents collection may be big, such that loading all documents for each PostalDocumentsPackage may be a bottleneck. Also you must consider whether you need change tracking on the Document entity in addition to the documents collection. If so, then you would need to implement change tracking for the Document class as well. Given that you're not using an ORM I would suggest solution #2 since solution #1 will lead you down a path of re-implementing change tracking, which among other things would pollute your domain classes. You may also consider a CQRS/Event Sourcing architecture in which change tracking is made explicit.

Resources