A Dac Extension Must Include The Publis Static Isactive Method

 

Hello everybody,

today  I want to share one line of code for Acuminator for error message:

PX1016 A DAC extension must include the public static IsActive method with the bool return type. Extensions which are constantly active reduce performance. Suppress the error if you need the DAC extension to be constantly active.

In case if you don't want to suppress Acuminator with a comment, you can do something like this inside of your extension:

public static bool IsActive() => true;

Certainly it is not the most elegant way of doing that, as better way could be usage of some attribute for this purpose, or for example use inheritance, but as of now, the smallest amount of code, you can use that line of code which is presented here. 

 

 

How To Get Key Fields Of Dac Class In Acumatica

 

Hello everybody,

recently for me it was needed to find out all key fields of DAC class. Code below does this:

 

public List<string> GetKeyFieldsOfDAC(Type dacClass)
{
    var result = new List<string>();
    var properties = dacClass.GetProperties().ToList(); 
    
    foreach (PropertyInfo info in properties)
    {
        var attrs = info.GetCustomAttributes(true);
        foreach (object attr in attrs)
        {
            if (attr.HasProperty("IsKey"))
            {
                dynamic typedAttribute = attr;
                if (typedAttribute.IsKey)
                {
                    result.Add(info.Name);
                }
            }
        }
    }
 
    return result;
}

 

And HasProperty method implementation goes below:

 

public static bool HasProperty(this object objectToCheck, string property)
{
    try
    {
        var type = objectToCheck.GetType();
        var prop = type.GetProperty(property);
        if (prop != null)
        {
            return true;
        }
    }
    catch (AmbiguousMatchException// it means we have more then one property
    {
        return true;
    }
 
    return false;
}

 That is not the most elegant solution in my life, and if you want to suggest a better one, please feel free to suggest.

 

How To Avoid Copy Paste With Help Of Attributes In Acumatica

 

Hello everybody,

today I want to leave a short note on how to avoid Copy/paste with help of custom attributes. 

Imagine following scenario. You have some set of duplicated code, which you need to apply at FieldSelecting . One of the ways of achieving this can be creation of some class and method within this class, which will handle that functionality, and then just copy/paste creation of the instance of the class at any place, where you need to have that business logic applied.

But you can use another way. You can use custom attributes. You can create your attribute, and then use that attribute over all places, where you may have a need for calling your business logic. In order to work that properly you'll need to inherit your Attribute from PXEventSubscriberAttribute, IPXFieldSelectingSubscriber . For example you can accomplish it like this:

 

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.Method)]
public class SomeAttribute : PXEventSubscriberAttributeIPXFieldSelectingSubscriber
{
    protected Type _TargetField;
    protected Type _DacType;
    
 
    public SomeAttribute(Type dacType, Type targetField)
    {
        _DacType = dacType;
        _TargetField = targetField;
    }
 
    public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
var graph = sender.Graph;
//and some other logic
}

Also this can be applied to the field like this:

 public class YourDacClass

{

    [Some(typeof(DacClass), typeof(DacClass.someField))]
    [PXUIField(DisplayName = "Value")]
    public virtual string SomeField { getset; }

}

 

and that's it. Now you can have move your duplication logic to attribute SomeAttribute, and enjoy smaller amount of code.

Purpose Of Rowpersisting Event

 

Hello everybody,

today I want to leave a note on usage of RowPersisting event.

Quite often I see situations, when RowPersisting is used for making additional insertions to database. Also quite often I see cases when some additional inserts being performed to database. 

I want to warn against such an approach. Reason for that is that during RowPersisting event, Acumatica opens transaction scope. Because of that, additional readings from db, or additional persists to db in scope of RowPersisting may lead to performance degradation and even deadlocks. 

Purpose of RowPersisting event is kind of latest resort, in which you can modify your record before putting it to database. And it shouldn't be used for some other purposes. Other purposes of RowPersisting event is validate record before it was putted to database, or cancel commit operation through throwing of an exception.

 

 

 

 

 

Lightweight Persist To Database

 

Hello everybody,

today I want to describe following use case. Quite often it is needed to persist to database one or another DAC class, which is filled by some data. 

As usually I see people do this via hard coding of DAC class inside of the Graph. But today I want to share with you a way of persisting DAC class without hardcoding it as a view. 

In order to accomplish this, you can use following graph:

public class ImportEntitiesInsertion : PXGraph<ImportEntitiesInsertion>
{
    public string AddView(Type dacType)
    {
        var viewName = "_DYNAMIC_" + dacType.GetLongName();
        if (!this.Views.ContainsKey(viewName))
        {
            var command = BqlCommand.CreateInstance(typeof(Select<>), dacType);
            var newView = new PXView(thistrue, command);
            Views.Add(viewName, newView);
            Views.Caches.Add(dacType);
        }
        return viewName;
    }
}

 

After that, in some other place of the code, you can use this graph like this:

 

var graphForInsertion = PXGraph.CreateInstance<ImportEntitiesInsertion>();
var dacType = typeof(SOOrder); 
var viewName = graphForInsertion.AddView(dacType);
 
for (int i = 0; i < 10; i++)
{
    var newOrd = new SOOrder();
    graphForInsertion.Views[viewName].Cache.Insert(newOrd);
}
graphForInsertion.Persist();

 

 What I especially like about this approach, is that records will be persisted initially in the cache, and only after you'll call Persist, all bunch of records will be persisted to database.

 

 

 

How To Override Properly Creatematrixitems

 

Hello everybody,

today I want to leave a short snippet on how to override methods in CreateMatrixItemsImpl graph extension. Below goes code snippet you can use for this purpose:

 

public class CreateMatrixItemsImplExt : PXGraphExtension<CreateMatrixItems.CreateMatrixItemsImpl, CreateMatrixItems>
{
    public override void Initialize()
    {
        base.Initialize();
    }
}

With help of this code fragment you can override and customize a bit more Matrix management of Acumatica.
 

Execution Timeout Expired Error Message In Acumatica

 

Hello everybody,

today I want to leave a short note on Execution Timeout Expired. In case if you face it on your local dev environment, then consider adding following key in your web.config:

<add name="PXSqlDatabaseProvider" type="PX.Data.PXSqlDatabaseProvider, 
PX.Data" ... queryTimeout="100" />

With that addition, you'll be able to execute long running queries without timeout. 

NB

But keep in mind, that such addition can affect performance of Acumatica for some cases. So use that freely for your dev instance, and be careful with your production instance.

 

How To Make Selector For Csanswers

 

Hello everybody,

recently one of the colleagues asked me how to make selector from Attributes values. Also that request seem trivial, but still took some time, especially with usage of FBQL query to build. 

Below goes template you may use if you'll need some kind of selector for attributes by some predefined value:

 

public class SOOrderExt : PXCacheExtension<SOOrder>
    {
        public class Codes
        {
            public const string MediaCode = "MEDIACODE";

            public class mEdiaCode : BqlType<IBqlStringstring>.Constant<mEdiaCode>
            {
                public mEdiaCode() : base("mEdiaCode")
                {
                }
            }

            public const string OrdOrigin = "ORDORIGIN";

            public class ordOrigin : BqlType<IBqlStringstring>.Constant<ordOrigin>
            {
                public ordOrigin() : base("ORDORIGIN")
                {
                }
            }
        }

        [PXSelector(typeof(SearchFor<CSAnswers.value>.Where<CSAnswers.attributeID.IsEqual<Codes.ordOrigin>>))]
        [PXDBString(50)]
        public string SomeValue1 { getset; }

        [PXSelector(typeof(SearchFor<CSAnswers.value>.Where<CSAnswers.attributeID.IsEqual<Codes.mEdiaCode>>))]
        [PXDBString(50)]
        public string SomeValue { getset; }
    }

 

 For me it was also interesting to note, that in the past it was common to use for Selector combination of PXSelector with Search, but in FBQL you'll need PXSelector with SearchFor.

 

How To Connect Acumatica With Mysql At Api Level

 

Hello everybody,

today I want to leave a short note on how to connect Acumatica with MySql. Recently for me was needed to organize connection between API project which send requests to Acumatica and MySql database. In codebase I've seen following line:

 using MySqlX.XDevAPI.Relational;

 But when I've tried to find nuget package MySqlX, I've find nothing useful. After a bit of googling I've discovered that it is needed to have referenced MySql.Data package. After I've added MySql.Data package on my project, all MySqlX staff become active, and I was able to build and execute my project

 

Create Index With Include Section

 

Hello everybody.

Today I want to leave a short note on include section of SQL queries. Imagine that you see SQL Query similar to this:

 CREATE NONCLUSTERED INDEX [tt_CSAttributeGroup] ON [dbo].[CSAttributeGroup] ([CompanyID], [EntityClassID], [EntityType], [IsActive])  INCLUDE ([AttributeCategory], [CreatedByID], [CreatedByScreenID], [CreatedDateTime], [DefaultValue], [LastModifiedByID]) WITH (ONLINE = ON)

 And while part in the first brackets is clear, then second part Include is not very clear from the first point of view. 

What is difference between index which has INCLUDE section and index which doesn't have INCLUDE section?

Include section means, that values will be stored with the key itself, but will not be part of the key. 

In case of presented SQL query, which is used by Acumatica, key values will be created based on CompanyID, EntityClassID, EntityType, IsActive columns. They will be used for building nodes of the tree. And also each node of the tree besides having Key calculated will also have those columns: AttributeCategory, CreatedByID, CreatedByScreenID, CreatedDateTime, DefaultValue, LastModifiedByID .

How this fact may be used later?

Once SQL server will search for some field, based on CompanyID, EntityClassID, EntityType, IsActive columns in it's query, and will find them, SQL will not need to go to the data itself for columns AttributeCategory, CreatedByID, CreatedByScreenID, CreatedDateTime, DefaultValue, LastModifiedByID. But will read values of those columns from the tree itself. 

If to go further with illustration, then at storage level B+tree structure without include section will look like this:

but with include section it will look like this:

Summary

If to compare indexes with INCLUDE and indexes without INCLUDE section, you can make following conclusion: indexes with include section will make faster reading from database. But they will cause longer insert/update operations, as each change will cause copy/paste overhead. And if you have a lot of columns included, then each insert/update will have double loading on your system.