Have a moderate size (40-odd function) C API that needs to be called from a C# project. The functions logically break up to form a few classes that will be API presented to the
Think of P/Invoke as platform invocation, are you calling into something like the Win32 API which is very P/Invoke friendly or do you need to provide .NET bindings for an unmanaged library?
Since the wrapper typically is very thin a C++/CLI wrapper doesn't necessitate that you know C++/CLI specifically. What you need to know can be found in the language specification, it's an extensive documentation with lots of examples. P/Invoke is more of an nice to have feature for smaller well established existing libraries, but if the interface for calling into that library changes you'll run into a problem. With C++/CLI you can still have a public managed interface in your C++/CLI project that's exposed for managed code and handle changes to the C API more easily that way.
If you wanna get rid of the extra DLL you can always try ILMerge, but I'm not sure if it's capable of handling mixed assemblies, (apparently not), but it looks like it's possible to link both managed and unmanaged *.obj files with the PlatformSDK linker like this:
cl.exe /MD /c /clr Unmanaged.cpp
csc.exe /target:module /addmodule:*.obj Managed.cs
link.exe /DLL /LTCG /NOENTRY /CLRIMAGETYPE:IJW *.obj Managed.netmodule