Xamarin Linking Sdk Assemblies Only - using AssemblyName vs AssemblyName.Class.Method - c#

I have a DLL MyAssemblyOne.dll which only contains one class with static methods:
namespace MyAssemblyOne
{
public class MyClassOne
{
public static string MyStaticMethod()
{
...
}
}
}
All is good so far, the assembly MyAssemblyOne.dll is generated.
Now I have another DLL, MyAssemblyTwo.dll which has a dependency on MyAssemblyOne.dll and uses it like:
no using here;
namespace MyAssemblyTwo
{
public class MyClassFromAssemblyTwo
{
public string SomeRandomMethod()
{
...
var smth = MyAssemblyOne.MyClassOne.MyStaticMethod();
...
}
}
}
Now I create a Xamarin project with Linking set to Sdk Assemblies Only and Use Shared Runtime disabled(basically Release mode), and I add my two DLLs - MyAssemblyTwo.dll and MyAssemblyOne.dll. The app builds ok, but when I run it I get something like:
cannot find MyAssemblyOne.dll.
Please note that this works if the Linking option is set to None.
However, if I change MyAssemblyTwo usage of MyAssemblyOne to be:
using MyAssemblyOne;
namespace MyAssemblyTwo
{
public class MyClassFromAssemblyTwo
{
public string SomeRandomMethod()
{
...
var smth = MyClassOne.MyStaticMethod();
...
}
}
}
everything works fine even with the Linking set to Sdk Assemblies Only.
How does the linker work? Why if I have a using statement everything is fine, but if I use the assembly name directly in the code it breaks.
It is worth mentioning that MyAssemblyOne and MyAsseblyTwo are .netstandard20 projects.

Related

Adding Akavache Static Linker class or Initializer error

I'm currently using Visual Studio Mac 2019 for to build my iOs Xamarin Forms Application.
My Application Akavache to store persistent data specifically credentials which I utilizes its BlobCache.Secure storage, but sadly the data doesn't persist.
I found that I should add either of the following:
1. Linker Class
using System;
using Akavache.Sqlite3;
namespace NameSpace.iOS
{
[Preserve]
public static class LinkerPreserve
{
static LinkerPreserve()
{
var persistentName = typeof(SQLitePersistentBlobCache).FullName;
var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;
}
}
public class PreserveAttribute : Attribute
{
}
}
or
2. Initializer
Akavache.Registrations.Start("FollowTheDrop");
Akavache: saved value not available after iOS app restart
but every time I add the solution above the following error below occurs during the build
MTOUCH : error MT2101: Can't resolve the reference 'System.Int32
SQLitePCL.raw::sqlite3_bind_blob(SQLitePCL.sqlite3_stmt,System.Int32,System.Byte[])',
referenced from the method 'System.Void
Akavache.Sqlite3.BulkInsertSqliteOperation/<>c__DisplayClass7_0::b__0()'
in 'SQLitePCLRaw.core, Version=1.1.13.388, Culture=neutral,
PublicKeyToken=1488e028ca7ab535'.
Am I missing something that causes this error?
It was solved by updating the following Nuget Packages below which has dependencies on each other:
Akavache 9.0.1
Splat 14.3.1
fusillade 2.4.47
Lastly when adding the linker static class consider adding a Preserve Attribute as shown below:
[Preserve]
public static class LinkerPreserve
{
static LinkerPreserve()
{
var persistentName = typeof(SQLitePersistentBlobCache).FullName;
var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;
}
}
public class PreserveAttribute : Attribute
{
}

grab UserSecrets from a NuGet package

I've written a C# class library for my company to use internally, and it uses DotNet UserSecrets to allow each developer to have their own credentials set without needing to worry about accidentally committing them. It worked fine during testing, but after installing it as a NuGet package as opposed to a project dependency, it no longer seems to be able to read from the secrets.json file. I'm wondering if this is a security thing that C# prevents, or if I need to do something else to enable that functionality in an external package.
The package code looks like this:
using Microsoft.Extensions.Configuration;
using TechTalk.Specflow;
namespace Testing.Utilities
{
[Binding]
public class Context
{
private static IConfigurationRoot configuration { get; set; }
private static FeatureContext feature_context;
// SpecFlow attribute runs this before anything else executes
[BeforeFeature(Order = 1)]
private static void SetFeatureContext(FeatureContext context)
{
try
{
configuration = new ConfigurationBuilder()
.AddUserSecrets<Context>()
.Build();
}
catch { }
feature_context = context;
test_context = context.FeatureContainer.Resolve<TestContext>();
}
public static string GetSecretVariable(string name)
{
object v = null;
// if the user secrets were found
if (configuration != null)
{
v = configuration[name];
}
if (v == null)
{
Logger.Warning($"secret variable '{name}' not found");
return null;
}
return v.ToString();
}
}
}
And in the calling code which always gets Null from the getter method:
using Testing.Utilities; // via NuGet package
namespace Testing
{
public static void Main()
{
System.Console.WriteLine($"found {Context.GetSecretVariable("super_secret")}");
}
}
Update:
It works as expected when I drag my locally built .nupkg file into my NuGet package cache and replace the one pulled from the repo. I updated the version number and pushed the change so I know they are on the same version, and it still only worked when I manually inserted my build. Now I'm more confused...
I ported the project from .NET Framework 4.6.1 to .NET 6 and it seemed to fix it. Kinda drastic change, but easy enough refactor and 461 is EOL anyways.

Source Generator not seeing classes in same project

I have a Source Generator, it looks like this:
public class Generator : ISourceGenerator
{
public Generator()
{
// Uncomment if you want to debug this
Debugger.Launch();
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
var syntaxReceiver = (MySyntaxReceiver)context.SyntaxReceiver;
var responseMessageClasses = syntaxReceiver.ResponseMessageClasses;
// do stuff to responseMessageClasses
}
}
internal class MySyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> ResponseMessageClasses { get; private set; } = new List<ClassDeclarationSyntax>();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax cds &&
cds.Identifier.ValueText.EndsWith("ResponseMessage", StringComparison.OrdinalIgnoreCase))
{
ResponseMessageClasses.Add(cds);
}
}
}
In the same project, I have a folder of simple classes of which the name all ends with "ResponseMessage". These classes only have simple types like string, int and bool as properties. They are in a different namespace than the SourceGenerator.
There's a second console app project that references my source generator as an Analyzer. When I build the project I can succesfully break into the SourceGenerator code.
The problem: the simple ResponseMessage classes never get seen by the SyntaxReceiver class I have. The OnVisitSyntaxNode method only hits the Program.cs file. What is going on?
By design, it's not possible to use a source generator in the assembly where it was defined. According to the design spec:
Generator implementations are defined in external assemblies passed to the compiler using the same -analyzer: option used for diagnostic analyzers.
...
Since generators are loaded from external assemblies, a generator cannot be used to build the assembly in which it is defined.

How to load an embedded resource from a base class that is in another VS project from that base class

Trying to embed an XSD in a base class assembly that is used by multiple derived class assemblies.
Visual Studio 2013 project A:
namespace A.B.C
{
public abstract class WidgetBase {
protected virtual void LoadSchema() {
var schemas = new XmlSchemaSet();
using (var resourceStream = this.GetType().Assembly.GetManifestResourceStream("A.B.C.Schemas.SchemaA.xsd"))
{
...Load the schema...
}
}
}
}
In csproj 2 I have a class that uses that base class:
Visual Stdio 2013 project B
namespace D.E
{
public class WidgetDerived : WidgetBase {
public string DoSomethingWithXml(string xmlFile)
{
var schema = base.LoadSchema();
...do something...
}
}
}
The problem is that I cannot find the XSD in object browser or setting the resource path to:
var resourceStream = this.GetType().Assembly.GetManifestResourceStream("A.B.C.Schemas.SchemaA.xsd")
var resourceStream = typeof(WidgetBase).Assembly.GetManifestResourceStream("A.B.C.Schemas.SchemaA.xsd")
var resourceStream = typeof(WidgetBase).Assembly.GetManifestResourceStream("D.E.Schemas.SchemaA.xsd")
The code runs fine if I load the XSD from a relative disk path. As an embedded resource though I'm not quite sure where it's going. I would think it would be embedded in the base class assembly and therefore visible to itself.
I've seen other answers for using GetManifestResourceStream when the resource is in another assembly and in the same assembly (here and https://support.microsoft.com/en-us/kb/319292 and many others) but not when the base class is loading its own assembly after a call from a derived class. In theory it should be the exact same as when loading from self, but it's not working that way.
Crud. The thing I tried first, but had a typo:
var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("A.B.C.Schemas.FuzzyTestPlan.xsd")
So, FYI, it does use the namespace and path of the relative assembly to the code. Just have to make sure you type it out correctly. :s

Running into trouble with MEF

I'm trying to create an extensible "utility" console application in .NET 4, and I figured using MEF to do this would give me the best in terms of flexibility and extensibility.
So I started setting up a MEF interface:
public interface IUtility
{
string Title { get; }
string Version { get; }
void Execute(UtilContext context);
}
And then I created two nearly identical test plugins - just to see how this stuff works:
MEF Plugin:
[Export(typeof(IUtility))]
public class Utility1 : IUtility
{
public string Title
{
get { return "Utility 1"; }
}
public string Version
{
get { return "1.0.0.0"; }
}
public void Execute(UtilContext context)
{
}
}
The console app that acts as the "host" for the MEF plugins looks something like this:
[ImportMany]
public IEnumerable<IUtility> _utilities { get; set; }
public void SetupMEF()
{
string directory = ConfigurationManager.AppSettings["Path"];
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(directory));
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
I checked - the directory is being read from the app.config correctly, and the plugin modules (*.dll files) are present there after solution has been built. Everything seems just fine..... until I get this exception:
System.Reflection.ReflectionTypeLoadException was unhandled
Message = Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
LoaderException:
Method 'get_Version' in type 'Utility.Utility1' from assembly 'Utility1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation
Hmmm.... what exactly is MEF trying to tell me here? And how do I fix this problem? Any thoughts, ideas, pointers?
Did I break some convention by having a property called Version of my own? Is that something reserved by MEF?
Sorry guys - my bad. There was a very old *.dll lingering around in the "plugin" directory that indeed did not have any implementation for that property's Get method.
Wiping out that pre-alpha ;-) *.dll solved my problem. I can indeed load my plugins now, and they can have a property called Version without any problems.

Categories