C# plugin with Entity Framework MariaDB

C# plugin with Entity Framework MariaDB

mdoddKFBCF
Participant Participant
1,625 Views
7 Replies
Message 1 of 8

C# plugin with Entity Framework MariaDB

mdoddKFBCF
Participant
Participant

I've spent a lot of time on this, and I have tried searching here fruitlessly, so I apologize in advance if this has been asked and answered before, but I'm at the end of my knowledge.

 

We essentially want to build an application that will load a block (or drawing) with attributes and populate those attributes with data read from a database. Initially we had envisioned this as a standalone exe, but I quickly realized in process was likely what I wanted. I was able to figure out how to populate attributes in a block, so I then tried to add the database connection to the application. As we have a few other tools (non Acad) that utilize Entity Framework to access a database we wanted to maintain this functionality. Also, ORM's are clean and appealing. I simply could not get it to work, so I backed up a step and started with a simple helloworld autocad program as below.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
//using Autodesk.AutoCAD.DatabaseServices;
//using Autodesk.AutoCAD.Geometry;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace wtf2
{
    public class wtf
    {
        [CommandMethod("holaworld")]
        public void HolaWorld()
        {
            Document doc = AcadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;

            ed.WriteMessage("Hola world! ... it works ... ok... great...");
        }

    }
}

 

At this stage I've simply added references to accoremgd.dll, acdbmgd.dll, acmgd.dll and everything works. But, EF is not installed yet so that's the next step and that is where things go wrong.

 

It took me a long time to figure out version compatibility with entity framework and autocad development, but I finally figured out that AutoCad depends on Framework and I cannot use .NET6 so I created a dll project using Framework 4.8.1. I then used NuGet to install entity framework. I am connecting to an existing database so I want to scaffold that so I install: Microsoft.EntityFrameworkCore.Tools (v3.1.31 for compatibility with Framework 4.8.1) and that installs a number of dependencies, including the requisit Microsoft.EntityFrameworkCore.Design and Microsoft.EntityFrameworkCore, both version 3.1.31. As this is going to connect to a MariaDB database I then use nuget to install MySql.EntityFrameworkCore (v3.1.27).

 

I then run the following scaffold command, edited for my particular use case, and it works. The EntityFramework code has been created and it is located where I want to use it.

 

Scaffold-DbContext "connection_string" "MySql.EntityFrameworkCore" -OutputDir <dir-name> -Tables <tables> -Context <context-name> -f

 

So, without making any additional changes or doing anything with the database code, I build the project (successfully) and then load the resulting dll into AutoCad 2023 with NETLOAD, but now I get error message within AutoCad

 

Cannot load assembly. Error details: System.IO.FileNotFoundException: Could not load file or assembly 'System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
File name: 'System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
   at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
   at System.Reflection.RuntimeAssembly.GetExportedTypes()
   at Autodesk.AutoCAD.ApplicationServices.AutoCADApplicationHolder.Initialize(Assembly assembly)
   at Autodesk.AutoCAD.ApplicationServices.ExtensionLoader.ProcessAssembly(Assembly assembly)
=== Pre-bind state information ===
LOG: DisplayName = System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 (Fully-specified)
LOG: Appbase = file:///C:/Program Files/Autodesk/AutoCAD 2023/
LOG: Initial PrivatePath = NULL
Calling assembly : Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\Program Files\Autodesk\AutoCAD 2023\acad.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2023/System.Threading.Tasks.Extensions.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2023/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2023/System.Threading.Tasks.Extensions.EXE.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2023/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.EXE.
LOG: Attempting download of new URL file:///Z:/Matalino Design/Projects/Duco Development/LoopDrawings/code/wtf2/wtf2/bin/Debug/System.Threading.Tasks.Extensions.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Revision Number
LOG: Attempting download of new URL file:///Z:/Matalino Design/Projects/Duco Development/LoopDrawings/code/wtf2/wtf2/bin/Debug/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.DLL.
LOG: Attempting download of new URL file:///Z:/Matalino Design/Projects/Duco Development/LoopDrawings/code/wtf2/wtf2/bin/Debug/System.Threading.Tasks.Extensions.EXE.
LOG: Attempting download of new URL file:///Z:/Matalino Design/Projects/Duco Development/LoopDrawings/code/wtf2/wtf2/bin/Debug/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.EXE.

 

I googled around a while on this and I am completely stuck.

 

The version of System.Threading.Tasks.Extensions that NuGet added to my project is 4.2.0.1. I've confirmed this by looking at that dll in my project references in Studio Community. Additionally nuget created app.config and packages.config files within my project. app.config contains the following relevant section

 

<dependentAssembly>
   <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
   <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" /></dependentAssembly>

and packages.config contains:

  <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net481" />

 

Finally, in my wtf2.csproj file I have the following:

<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
    </Reference>

And this seems consistent within my project and seems like what is supposed to be there based on what I've searched.

 

So, I'm at my wits end. Everything in my project is pointing at 4.2.0.1 dll, but AutoCad is giving an error referencing 4.2.0.0 dll, and I simply have no idea what this means or what I should do. I've tried to provide an exhausting level of detail and this should be fully recreatable (except for the scaffold).

0 Likes
Accepted solutions (1)
1,626 Views
7 Replies
Replies (7)
Message 2 of 8

norman.yuan
Mentor
Mentor

If I were you, there are a few things I would consider:

 

1. At current advancement of the .NET technologies, I'd try not to have AutoCAD to directly connect to a database when the database is a server-based. Always provide data access through service layer (typically HTTP services), so that the database access is unified to all kinds of applications (AutoCAD, or other custom apps you would have) that may need to have access the same set of data. I'd only consider to let AutoCAD directly talk to the database when the data set is small, CAD only and hosted in file-based database. 

 

2. If you do decide to let your CAD app to depend on direct DB server access, then I would only use Entity Framework 6, which is included with .NET Framework, and not choose "out-dated" MS EntityFrameworkCode.Tools 3.xxx, because it is only claimed to support .NET Standard 2.0, which, even theoretically supported by .NET framework, the extra package installation may cause issues (as what you are experiencing).

 

3. If you still decide to go with the "not-so-good-option" - direct DB serve access and EntityFrameworkCore.Tools 3.xxx, then you have already know that you need to apply the app.config required because of using EntityFrameworkCore.Tool 3.xxx to AutoCAD. Remember, the CAD plugin is a DLL, the app.config in the DLL project would not be loaded automatically when the DLL is loaded into AutoCAD. You need to migrate the configurations in the DLL's app.config into AutoCAD's configuration (acad.exe.config). So, you want try to copy the content in your DLL's app.config into acad.exe.config. Now, you can see what I said above makes sense.

 

So, since your database is  server-based, I strongly recommend you separate AutoCAD app (and other apps) from the database with a service layer, and you  can do your data access with latest .NET technology (.NET6, or even newly released .NET7) while your CAD plugin stay with .NET framework with minimum dependencies.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 8

daniel_cadext
Advisor
Advisor

“We essentially want to build an application that will load a block (or drawing) with attributes and populate those attributes with data read from a database.”

 

Can you use ODBC?

I wrote an ODBC Field evaluator here in C++ https://www.theswamp.org/index.php?topic=57864.0

That may be what you’re looking for, just put ODBCFields in your attributes

It’s kinda beta tho. 

 

full disclosure, I plan post this on the Autodesk store at some point, but I’ll hook you up because I need a beta tester : )

if not… no worries

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 4 of 8

mdoddKFBCF
Participant
Participant

Norman - I appreciate your detailed response. It gave me a lot to think about. I should mention that I do not consider myself a C# developer. I've dabbled, but most of my experience is in python as a data scientist. Having said that I have a number of questions about your post and I have tried my hand at one interpretation, which I will discuss below. I couldn't s

 


1. At current advancement of the .NET technologies, I'd try not to have AutoCAD to directly connect to a database when the database is a server-based. Always provide data access through service layer (typically HTTP services), so that the database access is unified to all kinds of applications (AutoCAD, or other custom apps you would have) that may need to have access the same set of data. I'd only consider to let AutoCAD directly talk to the database when the data set is small, CAD only and hosted in file-based database. 

Could you elaborate on this? Are you suggesting we would be better to build a rest API for the database? That's how I am interpreting your HTTP services comment.

 


2. If you do decide to let your CAD app to depend on direct DB server access, then I would only use Entity Framework 6, which is included with .NET Framework, and not choose "out-dated" MS EntityFrameworkCode.Tools 3.xxx, because it is only claimed to support .NET Standard 2.0, which, even theoretically supported by .NET framework, the extra package installation may cause issues (as what you are experiencing).


Yeah, this is exactly what I would like to do, but I honestly cannot figure out how to do it. My understanding is that I cannot build a .Net 6 addon to AutoCad and EF6 depends on .NET6 so I guess I don't understand what you mean here.

 

3. If you still decide to go with the "not-so-good-option" - direct DB serve access and EntityFrameworkCore.Tools 3.xxx, then you have already know that you need to apply the app.config required because of using EntityFrameworkCore.Tool 3.xxx to AutoCAD. Remember, the CAD plugin is a DLL, the app.config in the DLL project would not be loaded automatically when the DLL is loaded into AutoCAD. You need to migrate the configurations in the DLL's app.config into AutoCAD's configuration (acad.exe.config). So, you want try to copy the content in your DLL's app.config into acad.exe.config. Now, you can see what I said above makes sense.


Yeah, I was sort of coming to a conclusion along these lines, but your explanation makes it much clearer. I agree with you completely this sounds bad and not something we want to do.

 


So, since your database is  server-based, I strongly recommend you separate AutoCAD app (and other apps) from the database with a service layer, and you  can do your data access with latest .NET technology (.NET6, or even newly released .NET7) while your CAD plugin stay with .NET framework with minimum dependencies.

 


Yeah, this does sound like something I'd be interested in, but I can't see what this solution actually looks like. Do I build a class library for the database and reference that within my AutoCAD class library or...?

 

What I tried...

 

Based on the last point you made I did try to abstract the entity framework from my AutoCad HolaWorld.

  1. I created a new solution.
  2. I added a new class library project for my simple holaworld Autocad DLL targeting framework 4.8.1. I confirmed that this worked.
  3. I added a new class library project for my entity objects targeting .Net 6 and EF 6. I scaffolded the database into this project.
  4. I added a new class library project targetting .Net6 to function as a data access layer. In this project I placed my EF6 context object and a new class "DBDataLoader" to interface with. In this project I add a reference to <3>.
  5. I added a new Windows Forms project targeting .Net6 to test <3> and <4>. In this project I reference to <4>. The point of this project is simply to test what I've learned so far and to make sure 3 and 4 are working. It is successful and I am able to read from the DB and write to a text box.
  6. Now I try adding a reference to <4> in <1> (ACAD DLL) and I am unable to build this project as I get an error message "Project x targets '.net6.0'. It cannot be referenced by a project that targets '.NetFramework,Version=v4.8.1'".

So, I'm not fully tracking what you have suggested, although I want to understand it and I think you are pointing me in the right direction and I am failing to grasp something.

0 Likes
Message 5 of 8

mdoddKFBCF
Participant
Participant

Daniel,


There is also an Excel file we will be grabbing data from and merging with the database data. Additionally, we are going to be creating hundreds of drawings each referencing different data from the databse (same general fields, but differe "WHERE TAG == x", so I don't think this would work. 

 

 

0 Likes
Message 6 of 8

daniel_cadext
Advisor
Advisor

 

That’s cool, if you still have problems with your code. Try installing MariaDB ODBC Drivers and use C#’s System.Data.Odbc.OdbcConnection. It’s fast, you can transfer .DWG’s as a blob etc. best thing is, you’re module code won’t be so dependent, you can query your excel file too.

 

Cheers 🙂

Dan

 

 

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 7 of 8

norman.yuan
Mentor
Mentor
Accepted solution

@mdoddKFBCF wrote:

... ...

What I tried...

 

Based on the last point you made I did try to abstract the entity framework from my AutoCad HolaWorld.

  1. I created a new solution.
  2. I added a new class library project for my simple holaworld Autocad DLL targeting framework 4.8.1. I confirmed that this worked.
  3. I added a new class library project for my entity objects targeting .Net 6 and EF 6. I scaffolded the database into this project.
  4. I added a new class library project targetting .Net6 to function as a data access layer. In this project I placed my EF6 context object and a new class "DBDataLoader" to interface with. In this project I add a reference to <3>.
  5. I added a new Windows Forms project targeting .Net6 to test <3> and <4>. In this project I reference to <4>. The point of this project is simply to test what I've learned so far and to make sure 3 and 4 are working. It is successful and I am able to read from the DB and write to a text box.
  6. Now I try adding a reference to <4> in <1> (ACAD DLL) and I am unable to build this project as I get an error message "Project x targets '.net6.0'. It cannot be referenced by a project that targets '.NetFramework,Version=v4.8.1'".

So, I'm not fully tracking what you have suggested, although I want to understand it and I think you are pointing me in the right direction and I am failing to grasp something.


1. Yes, by "service layer", at current trend, it usually means REST API service via HTTP. You would say, "well, I have to host the service somewhere", but you are hosing the database somewhere, then why hosing a service would be an issue, considering you will have (or already have had) multiple apps to access the same data.

 

2. My suggestion to use EF6 instead of EF Core 3.xxxx, is because EF6 was designed for .NET Framework, not .NET Core/Standard (while it can be used with .NET Core). Do not be fooled by the "6": it is not related to .NET6, while is .NET Core was named well after EF6. Well, a small correction to what I have said: EF6 is a separate download/installation from .NET Framework, but it was designed mainly for targeting .NET Framework.

 

As for the steps you have tried:

1. OK.

2. OK.

3. Wrong. You should still create a .NET Framework class library project, the same as step 2. The you get Nuget package of EF6.x.

4 to 6: since it was wrong on step 3, the following steps would obviously not work.

 

Also, to be warned: while I suggest to use EF6.x instead of EF Core 3.xxx (again, because EF6 was designed for .NET framework and could work wither earlier version of .NET Core, while EF Core 3.xxx is designed for earlier version of .NET Core and could work with .NET Framework. So, for me, the choice is obvious for .NET Framework-based Cad Plugin apps), you STILL HAVE TO DEAL with the app.config issue, as I suggested: you must migrate the EF required configuration in the app.config into AutoCAD's acad.exe.config. Have you tried that with your current EF Core 3.xxx solution?

 

Hope this clarifies what I said previously a bit more.

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 8 of 8

mdoddKFBCF
Participant
Participant

Eventually, we moved forward with creating a service layer to get the data and create a JSON file for all of the data AutoCAD needs to consume.

0 Likes