Middle Tier plugin issues

This forum is for programmers who have questions about the source code.
Post Reply
User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Middle Tier plugin issues

Post by wjstarck » Sat Nov 09, 2019 2:24 pm

Hello again-

What is the correct method for sending queries from within a plugin with the Middle Tier?

I have a lot of them in my ConvertAnesthDatabase.cs and I'm running up against this in revamping my plugin to work with the Middle Tier:

Code: Select all

			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
				throw new ApplicationException("No longer allowed to send sql directly.  Rewrite the calling class to not use this query:\r\n"+command);
			}
Since my ConvertAnesthDatabase.cs is all queries, can you give me an example of how to change it for the ClientWeb RemotingRole?

For example :

Code: Select all

			string command = "SELECT ValueString FROM preference WHERE PrefName='AnesthPlugin_DataBaseVersion'";//notice the very unique name.
			if (table.Rows.Count == 0) {//This plugin has never run before.
				command = "INSERT INTO preference (PrefName,ValueString,Comments) VALUES('AnesthPlugin_DataBaseVersion','" + POut.String(versionNum) + "','Anesthetic record & scheduled medication inventory system for Open Dental <BigIdeaSoft.com>')";
				DataCore.GetTable(command);
				FromVersion = new Version(1, 0);
			}
			else {
				FromVersion = new Version(table.Rows[0][0].ToString());
			}
Thanks.
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Mon Nov 11, 2019 7:48 am

If you have a method that makes a database call, you will need to put what we call a "remoting role check" at the beginning of the method. For example in OpenDentBusiness.Patients.GetPat, there is:

Code: Select all

if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
	return Meth.GetObject<Patient>(MethodBase.GetCurrentMethod(),patNum);
} 
You can use a different method that GetObject if your method returns something different such as GetBool, GetLong, Get Void, etc. You will also need to make sure that every parameter to the method is passed as an argument to GetObject. Another thing, you'll have to make sure that the namespace of your class matches the name of your assembly. If my plugin was compiled as CrazyChris.dll, all my classes that I use over middle tier would have to be in the CrazyChris namespace.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Mon Nov 11, 2019 5:08 pm

Thanks Chris-

What do the remoting calls look like inside a void though, since I'm not returning anything?

All of the methods in my ConvertAnesthDatabase are voids :shock:
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Mon Nov 11, 2019 5:50 pm

Sure, you can look at OpenDentBusiness.Patients.Delete as an example.

Code: Select all

if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
	Meth.GetVoid(MethodBase.GetCurrentMethod(),pat);
	return;
}
The important thing is to include the return after the GetVoid line. If you don't, you will get a runtime exception.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Mon Nov 11, 2019 9:06 pm

Chris-

So I now I have:

Code: Select all

public static void Begin() {//unlike in the main program, this conversion script runs on every startup.
			if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) {
				Meth.GetVoid(MethodBase.GetCurrentMethod());
				return;
			}
			Version MainAppVersion = new Version(System.Windows.Forms.Application.ProductVersion);//eg. 6.8.0.0
			Version MainAppMajMin = new Version(MainAppVersion.Major, MainAppVersion.Minor);//eg. 6.8
			ThisAssemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;//eg. 6.7.0.0
			Version ThisAssMajMin = new Version(ThisAssemblyVersion.Major, ThisAssemblyVersion.Minor);//eg. 6.7
But that results in:

Code: Select all

Error loading Plugin:Anesthesia[VersionMajMin].dll}. Object reference not set to an instance of an Object
Which I tracked down to here:

Code: Select all

		private static string SendAndReceiveRecursive(IOpenDentalServer service,string dtoString,bool hasConnectionLost=true) {
			try {
			    return service.ProcessRequest(dtoString); <------------------
			}
			catch(WebException wex) {
on RemotingClient.cs

which returns:

Code: Select all

"<?xml version=\"1.0\" encoding=\"utf-16\"?><DtoGetVoid xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><Credentials><Username>Admin</Username><Password>myPassword</Password></Credentials><MethodName>Anesthesia.ConvertAnesthDatabase.Begin</MethodName><Params /><ComputerName>WJSBOX10</ComputerName></DtoGetVoid>"
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Tue Nov 12, 2019 7:36 am

It looks like you may be encountering two separate issues. When you get

Code: Select all

Error loading Plugin:Anesthesia[VersionMajMin].dll}. Object reference not set to an instance of an Object
You would get that message if your plugin dll exists and it either doesn't have a class named "Plugin" in the "Anesthesia" namespace or the constructor for "Plugin" threw an exception. I would put a breakpoint on OpenDentBusiness.Plugins line 112 and see if you can tell what's going on.

When you call

Code: Select all

return service.ProcessRequest(dtoString);
you are making a web call to the middle tier server. It sounds like the server is giving a different exception. Does that line throw an exception? If not, was is the return result?
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Tue Nov 12, 2019 8:47 am

I have a Plugin.cs in my project

This seems to be where the exception is being thrown (RemotingClient.cs):

Code: Select all

		public static void ProcessGetVoid(DtoGetVoid dto,bool hasConnectionLost=true) {
			string result=SendAndReceive(dto,hasConnectionLost);
			if(result!="0"){
				DtoException exception=(DtoException)DataTransferObject.Deserialize(result);
				throw ThrowExceptionForDto(exception); <-----------------------------
			}
		}
In the exception

Code: Select all

ComputerName = null
Credentials = null
Error Code = 0
Exception Type = "Null reference exception"
Message ="Object reference not set to an instance of an object"
MethodName = null
Params = null
Hopefully that indicates something
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Tue Nov 12, 2019 10:23 am

Ok, that means you're definitely getting an error on the middle tier server side. As you can see, our middle tier errors are not very informative, unfortunately. To troubleshoot this, I would run the middle tier service in debug and put a breakpoint at DtoProcessor.ProcessDto to figure out what's causing the error. Let me know if you'd like more details on running middle tier in debug.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Tue Nov 12, 2019 10:57 am

Yes, sounds great thanks.

I'm going to set up MIddle Tier on my development machine - I can run that without SSL, right?
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Tue Nov 12, 2019 11:18 am

Yes, you can use HTTP.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Tue Nov 12, 2019 11:57 am

OK, I'm getting the same behavior on my development server.

Let me know how to debug the Middle Tier

Thanks Chris
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Tue Nov 12, 2019 1:36 pm

Ok,

1. In the Open Dental solution, in the solution properties, set multiple startup projects to include OpenDental and OpenDentalServer.
2. In the properties for the OpenDentalServer, change the drop down that says "IIS Express" to "Local IIS". You will need to be running VS as admin. You'll also need IIS features enabled through Windows.
3. Click "Create Virtual Directory". Note what the Project Url points to. It will probably default to "http://localhost/OpenDentalServer".
4. In File Explorer, go to the location of the OpenDentalServer project. Modify OpenDentalServerConfig.xml to connect to your database.
5. In VS, hit Start to launch Open Dental and the middle tier server. In the Choose Database window in the URI textbox, take the Project Url from step 3 add append "/ServiceMain.asmx".
6. You should now be able to hit breakpoints on the middle tier server. DtoProcessor.ProcessDto is they entry point for all middle tier requests.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Tue Nov 12, 2019 5:42 pm

Hi again Chris-

It seems that the error being thrown by the server is

Code: Select all

Sysem.IO.FileLoadException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest does not match the assembly reference.[Exception from HRESULT 0x80131040]
Now I'm really baffled :evil:

Here are the Assembly Load and Stack Traces

Code: Select all

Assembly Load Trace: The following information can be helpful to determine why the assembly 'Anesthesia' could not be loaded.


=== Pre-bind state information ===
LOG: DisplayName = Anesthesia
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: Anesthesia | Domain ID: 3
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/Users/wjs/Desktop/ODVersions/opendental19.3/opendental19.3/OpenDentalServer/
LOG: Initial PrivatePath = C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\OpenDentalServer\bin
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\OpenDentalServer\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/opendentalserver/376dd42c/cf53ac54/Anesthesia.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/opendentalserver/376dd42c/cf53ac54/Anesthesia/Anesthesia.DLL.
LOG: Attempting download of new URL file:///C:/Users/wjs/Desktop/ODVersions/opendental19.3/opendental19.3/OpenDentalServer/bin/Anesthesia.DLL.
WRN: Comparing the assembly name resulted in the mismatch: NAME
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

Stack Trace:


[FileLoadException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +0
   System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +36
   System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +152
   System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection) +77
   System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +21
   System.Reflection.Assembly.Load(String assemblyString) +28
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +38

[ConfigurationErrorsException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +738
   System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +217
   System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +130
   System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +170
   System.Web.Compilation.BuildManager.GetPreStartInitMethodsFromReferencedAssemblies() +92
   System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded) +290
   System.Web.Compilation.BuildManager.ExecutePreAppStart() +157
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException) +549

[HttpException (0x80004005): Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +10074716
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +95
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Tue Nov 12, 2019 8:55 pm

OK, I think I'm on to something.

The middle tier appears to use the AssemblyName when loading dlls.

So in normal OD, I have to match the Assembly version with the running version of OD. In VS, I name the plugin Assembly Anesthesia19.3, and so when I build it outputs Anesthesia19.3.dll. This goes into the OD program folder, and then OD uses the plugin dll name trigger to strip off the version number from the version that matches the running version of OD, and leave us with just Anesthesia.dll in the OD Program folder.

Middle Tier however, uses AssemblyName when loading dlls, and will throw an exception when the AssemblyName doesn't match the dll name in the filepath.

I am able to work around this by changing the Assembly name in VS to just plain old Anesthesia, but this will cause issues down the line with having the correct plugin to load that matches the running version of OD.

Also, while I'm debugging the middle tier, the Application.ProductVersion and Application.StartupPath are taken from IIS, not OD. which throws code blocks like this off:

Code: Select all

			Version MainAppVersion = new Version(System.Windows.Forms.Application.ProductVersion);//eg. 6.8.0.0
			Version MainAppMajMin = new Version(MainAppVersion.Major, MainAppVersion.Minor);//eg. 6.8
			ThisAssemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;//eg. 6.7.0.0
			Version ThisAssMajMin = new Version(ThisAssemblyVersion.Major, ThisAssemblyVersion.Minor);//eg. 6.7
			versionNum = ThisAssemblyVersion.ToString();
			versionNumMajMin = ThisAssMajMin.ToString();
			if (ThisAssMajMin > MainAppMajMin) {
				//Prevent the use of a new plugin with an old version of OD.  User should upgrade OD.
				throw new ApplicationException("Plugin version (" + ThisAssMajMin + ") must not be newer than OD version (" + MainAppMajMin + "). Please install the correct version.");
			}
			//In
In the above example, the MainAppVersion returns '10.0.1.1734' (inetsrv version) instead of the desired OD version 19.3.22.x.

So how do you handle that internally at OD when debugging the middle tier?
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Wed Nov 13, 2019 8:48 am

If you're on the middle tier server, you can use this to get the physical path:

Code: Select all

System.Web.HttpContext.Current.Server.MapPath(null)
I think I've found the bug in our code that is preventing the renaming of Anesthesia19.3.dll to Anesthesia.dll. I'll get it fixed.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Wed Nov 13, 2019 8:54 am

Thanks Chris
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Thu Nov 14, 2019 7:55 am

One more for good measure:

I get:

Code: Select all

Could not load file or assembly 'Newtonsoft.Json, Version 9.0.0.0 blah blah blah. The located assembly's manifest defintion does not match the assembly reference
I notice that in the OpenDental/Required DLLs folder the version there is 9.0.1.19813, but in VS, the reference in the OpenDental project shows the version as 6.0.0.0

The path to Newtonsoft.Json in OpenDental project is

Code: Select all

C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll
while the one in OpenDentBusiness is

Code: Select all

C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\Required dlls\Newtonsoft.Json.dll
Should I remake the reference in OpenDental to the Required DLLs one? Or,,,?
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Thu Nov 14, 2019 9:17 am

Our philosophy toward NuGet packages is to make a copy of the dll in our "Required dlls" folder and reference that dll directly. My guess is that in your plugin you're referencing Newtonsoft.Json that is a different version than the one the rest of Open Dental is referencing. If that's the case, I would recommend changing your reference to Newtonsoft.Json to the one in Required dlls.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Thu Nov 14, 2019 9:29 am

I don't reference NewtonsoftJson.dll in my project at all.

It seems that VS will copy the dll and references the wrong one in a config file which causes the error. The only workaround I have found it to use the package manager in VS to reinstall NewsoftJSon 9.0.1 which forces VS to rewrite the config files thus resolving the error.
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Thu Nov 14, 2019 2:30 pm

Would you mind adding the stack trace where you're getting "Could not load file or assembly"?

I have committed the fix for the bug regarding AnesthesiaXX.XX.dll not copying on middle tier. It will be available in 19.2.46 and 19.3.24. The problem was that on line 83 of Plugins.cs we were using Application.ProductVersion to get the current Open Dental version. When running the middle tier server, this was returning the version of IIS.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Thu Nov 14, 2019 2:50 pm

Hey Chris-

That update completely fixed the problem, thank you very much.

One more issue, if I may:

When I hit

Code: Select all

		public static void Load_end(FormOpenDental sender, long patNum) {
			if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) {
				Meth.GetVoid(MethodBase.GetCurrentMethod(),sender,patNum);
				return;
			}
On my plugin's FormOpenDentalA, I get:

Code: Select all

Error Message: Self referencing loop detected for property 'Parent' with type 'System.Windows.Forms.MainMenu'. Path 'Obj.Menu.MenuItems[0]'.
Stack Trace:
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
   at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, JsonSerializerSettings settings)
   at OpenDentBusiness.DtoObject.ConstructArray(MethodBase methodBase, Object[] objArray) in C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\OpenDentBusiness\Remoting\DtoObject.cs:line 217
   at OpenDentBusiness.Meth.GetVoid(MethodBase methodBase, Object[] parameters) in C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\OpenDentBusiness\Remoting\Meth.cs:line 316
   at Anesthesia.FormOpenDentalA.Load_end(FormOpenDental sender, Int64 patNum)
   at Anesthesia.Plugin.HookAddCode(Object sender, String hookName, Object[] parameters)
   at OpenDentBusiness.Plugins.HookAddCode(Object sender, String hookName, Object[] parameters) in C:\Users\wjs\Desktop\ODVersions\opendental19.3\opendental19.3\OpenDentBusiness\Plugins\Plugins.cs:line 175
I've tried to get Newtonsoft.Json to ignore the self referencing loops but no success so far.

Any insight?
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Thu Nov 14, 2019 4:50 pm

cmcgehee wrote:Would you mind adding the stack trace where you're getting "Could not load file or assembly"?

I have committed the fix for the bug regarding AnesthesiaXX.XX.dll not copying on middle tier. It will be available in 19.2.46 and 19.3.24. The problem was that on line 83 of Plugins.cs we were using Application.ProductVersion to get the current Open Dental version. When running the middle tier server, this was returning the version of IIS.
Yup, and spoke too soon. Forgot that I had removed the Major and Minor builds from the filename in program links setup, so it's still crashing. It'll work as long as I only have Anesthesia19.3.dll in the folder, but if I need Anesthesia.dll in there, and in the past that has just been the last supported dll, just renamed (because OD wants to see the dll with the version stripped in the OD Program folder.

Here is the error and stacktrace:

Code: Select all

Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.IO.FileLoadException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Assembly Load Trace: The following information can be helpful to determine why the assembly 'Anesthesia' could not be loaded.


WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].

Stack Trace:


[FileLoadException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +0
   System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +36
   System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +152
   System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection) +77
   System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +21
   System.Reflection.Assembly.Load(String assemblyString) +28
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +38

[ConfigurationErrorsException: Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +738
   System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +217
   System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +130
   System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +170
   System.Web.Compilation.BuildManager.GetPreStartInitMethodsFromReferencedAssemblies() +92
   System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded) +290
   System.Web.Compilation.BuildManager.ExecutePreAppStart() +157
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException) +549

[HttpException (0x80004005): Could not load file or assembly 'Anesthesia' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +10074716
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +95
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Mon Nov 18, 2019 7:27 am

From the stack trace, it looks like you have Anesthesia.dll present in the folder "C:\Program Files (x86)\Open Dental\bin". When the first middle tier request comes in, it will try to load every dll in the bin folder. If you instead put the dll in "C:\Program Files (x86)\Open Dental", the code in Plugins.LoadAllPlugins will load your dll dynamically.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Mon Nov 18, 2019 10:53 am

Yes, those files were in the bin folder in my Solution as I running and debugging. So wouldn't the Middle Tier server see that folder as equivalent to C:\Program Files (x86)\Open Dental when I'm debugging? And I should be selecting the Debug_Web dropdown when I build, right? Also, when debugging, what should be in C:\MySolution\opendental19.3\OpenDentalServer\bin?

With regard to the Newtonsoft.Json error, I'm assuming I'm getting that circular reference error since Plugins.cs launches my plugin, and my plugin refers back to FormOpenDental to loop through the controls maybe? Googling is not much help, I don't seem to be able to use the {JsonIgnore] atrribute anywhere and the only other solution is to configure Json to ignore circular references globally, but I can't see anywhere that I can do that in OpenDentalServer project? :o

https://stackoverflow.com/questions/739 ... d-for-type
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Mon Nov 18, 2019 12:47 pm

The middle tier server sees "C:\Program Files (x86)\Open Dental" as equivalent to "C:\MySolution\opendental19.3\OpenDentalServer". Yes, Debug_Web is the right build configuration for what you're doing. When you're debugging, your "C:\MySolution\opendental19.3\OpenDentalServer\bin" should have OpenDentalServer.dll and OpenDentBusiness.dll along with all its dependencies. Your Anesthesia19.3.dll should be in "C:\MySolution\opendental19.3\OpenDentalServer" (and "C:\Program Files (x86)\Open Dental").

Regarding, your Newtonsoft error, I would recommend modifying your method so that is doesn't pass FormOpenDental over middle tier. Something like:

Code: Select all

    public static void Load_end(FormOpenDental sender, long patNum) {
         sender.Text="FormOpenDental with Plugin";
         Load_end_DatabaseWork(patNum);
    }

    public static void Load_end_DatabaseWork(long patNum) {
         if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) {
            Meth.GetVoid(MethodBase.GetCurrentMethod(),patNum);
            return;
         }
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Wed Nov 20, 2019 3:23 pm

Chris-

Brilliant, wouldn't have thought of that, thanks.

But now, in the Load_end_DatabaseWork remotingrole check, I get:

Code: Select all

{"ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment."}
Here:

Code: Select all

			Load_end_DatabaseWork(patNum);

		}				
		[STAThread]
		public static void Load_end_DatabaseWork(long patNum) {
			if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) {
				Meth.GetVoid(MethodBase.GetCurrentMethod(),patNum); <------------------------
				return;
			}
			PatCur = Patients.GetPat(patNum);
I tried adding the [STAThread] attribute above the method as you see here but I still get the exception.
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

User avatar
cmcgehee
Posts: 711
Joined: Tue Aug 25, 2015 5:06 pm
Location: Salem, Oregon

Re: Middle Tier plugin issues

Post by cmcgehee » Wed Nov 20, 2019 5:38 pm

My guess is that in the class in which Load_end_DatabaseWork lives, there is a static field that contains an ActiveX control. Try putting Load_end_DatabaseWork in its own class with nothing else and see if you still get the error.
Chris McGehee
Open Dental Software
http://www.opendental.com

User avatar
wjstarck
Posts: 935
Joined: Tue Jul 31, 2007 7:18 am
Location: Keller, TX
Contact:

Re: Middle Tier plugin issues

Post by wjstarck » Thu Nov 21, 2019 11:21 am

Yup.

Thanks Chris
Cheers,

Bill Starck, DDS
Big Idea Software, LLC
Developer, EASy(Electronic Anesthesia System) for Open Dental
817-807-1709
TX, USA

Post Reply