For those of you who attended the October meeting in Delafield WI, here are a few of the code examples that Josh and I talked about in the technical breakout session.
I have attached the data validation classes I use accross Epicor as an External DLL. It includes the HTML email templater demonstrated. I still need to work on the Visual Studio plugin so that it works with 10.1 but hopefully I can do something with that in the public form in the future. BPMs from BO push back into Epicor from Visual Studio but I have to fix DT and multi company. The auto pulling in of the CS filles and reference discovery works again.
@rbucek what sort of cool things could one do with VSTO plugin? I know the list could be limitless, but what are some useful/practical things we could gain? One thing I can think of is emulating the EPICOR module that allows you to add records to DB through email - which sounds pretty sweet!
It’s usually very business specific. Ours was driven by customer requirements. We have to have material traceability back to the steel mills and we get material test reports as pdf’s via email from our suppliers. We need to recall those by job, by assembly when we send out new products to our customers. Tracing these and recalling them from a file cabinet was a nightmare. So I built the pluggin to recognize when it got an email with an attachment of that type. It looked up the supplier by email in the supplier maintenance area of epicor, brought back relevant data, filled in fields on the custom outlook form that was only visible in emails that contained MTR attachments, it gathered other info from the subject line and body, automatically saved the attachment in our file server and created an index record in a UD table in Epicor. It was simple to write a dashboard at that point that prompted a job number and voila, a list of all MTR’S that they could print from within Epicor along with the assembly traceability. We took a task that required almost 20 man hours a week to manage into something that took an hour or two.
I’ll throw some guidelines I give to internal devs on GetDtlPOLineInfo.Post.EmailExample.cs. Some items are religious style so take those with a grain of salt. Other comments are from scars of picking up others code (I love the old saying - Always write your code like the person taking over is a serial killer with your address)
First reading the code is kind of difficult to pickup on areas due to the verbose nature. Putting in
‘foo.X == false’ is the same as !foo.X. Same goes for bar.Y==true. That’s just a bar.Y statement. It’s a true or false value already so no need to make the code longer and harder to read.
Along that same line there are some complex action statements - a couple repeated I think:
<…>
&& this.ttJobHead.Any(
r1 =>
r1.SysRowID == r.SysRowID
&& r1.Unchanged()
&& r1.JobReleased != (r.JobReleased)
&& r1.JobReleased == (false)));
what the heck is that comparison you are doing? Will someone know 6 months from now (or yourself next week?)
Refactor that out into its own method and reuse it. Especially when you are doing the same comparison in a couple of areas. If you change the logic at some point you may only fix one, not two. If you pull that out into a common method you prevent breaking things in the future. Furthermore you can name the method something explanatory and avoid the need to put in comments as you name the method what it does - self documenting.
Another positive in pulling out things into a common method is the ability to create Unit Tests to verify functionality. That way when you come back and fix things in 6 months, you run the tests and realize you just broke things - avoiding those long weekends when you mess up a deployment
That is also the reason to remove the goto statements and make those if then queries instead against refactored out methods Then those methods can again be unit tested…
Another item that makes things difficult to follow is the numerous parameters passed around:
<…>
Action<string, string, string, string, string, string, string, string, decimal, decimal, decimal, decimal, decimal, DateTime> InsertUDRecord
= (workOrder, action, type, jobNum, partNum, material, rev, opCode, asmbl, qty, rqdQty, oprSeq, mtlSeq, startdate)
<…>
Instead of passing these long lists of variables around, create a new class named appropriately to describe this structure and populate an instance, pass that around, etc. If you need to add a field then you change less code as well.
var UD32svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD32SvcContract>(Db);
This create an instance of the BO you want to call. That BO create a db connection. When you are done with the BO you should dispose it as soon as possible. If you don’t, it sits around until the end of the server call where the framework walks through all the BOs and libs laying around and cleaning them up. In most cases this might not be a bad but in the case of a report or process or long bo method this may suck up all available db connections until the dotNet garbage collector comes along in the background to clean things up. Think MRP or Posting Engine and those calls can take some time starving the app server from accessing the db.
Instead, wrap the stand up of BOs and Libs in using statements:
using (var UD32svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD32SvcContract>(Db))
{
<…>
This gives back the db connection and frees memory asap.
Take a look at the ImplFactory in Epicor.ServiceModel.dll. / Epicor.ServiceModel.Channels.ImplFactory.CreateImpl()
This has several overloads to create client proxies easily with most of the differing parameters of interest. It’s used by Task Agent, all Client BOs, Social, Search, …
GetCustomBinding() also will help with setting up all the various binding types ‘in the can’ for E10 -
Epicor.ServiceModel.Bindings.BindingName has constants for all those bindings.
NOTE - These have evolved over the releases so have more overloads from 10.0 to 10.1.400 to 10.1.500, etc.
@Bart_Elia lol that’s code Epicor stubs out from the condition element used in the BPM designer…so I don’t know? Not much i can do there. That being said, thank you so much, your feedback is great! Just what i needed to hear, lots of things to think about… the only code i wrote is in
so if you are working within a BPM and Epicor takes your custom code action, creates a method out of it, how do you refactor anything into it’s own method? I understand i could technically build a library, and make a bunch of classes with methods in it, reference that, but most of this is so specific, I’ll never ever use it anywhere else in the system? Just curious, I’m not a coder by background or trade, but i do enjoy doing it when the task requires it. There are a lot of variables being used in that action and it looks clunky yes, there’s that class question again, how to create that within the scope of the custom action code element, so much to learn lol, what are the limitations there and how to work within that?
Ahhh! Sorry, like I mentioned before if you are doing code in BPM I personally - (Not a company endorsement here but a personal feeling) - I personally think that if you go into writing code it would benefit more from creating an external assembly for BPMs and calling out to it for all the normal reasons - version control, unit testing, stability, etc.
I did not really look at the business process you are working at to see if you ‘need’ code as opposed to the BPM designer widgets. We have been slowly increasing the UI widgets available in BPM to try to make that more of an actionable designer and fit more customer scenarios - less code.
Now I’ll have to go back and load up your sample to look again
Hey Bart
What is the reason for doing your own DLL? My concern with going with external DLL’s is the constant re-compiling. If you use any of Epicor’s DLL’s in your custom DLL then that is tied to whatever Epicor version X. When you guys release a patch every 2 weeks… or a service pack I’ll have to constantly re-compile my custom DLL to work with the new version of the Epicor DLL. Am I wrong? do you guys not increment the versions of the DLLs with patches / service packs?
I’ve run into this issue several times, but maybe there is something I am missing.
Just curious if there is a better way to handle that.