In main library there is a namespace Main.TargetNamespace. In merged library there is a public type Merged.SourceNamespace.Type.
I need to move the type Merged.SourceNamespace.Type to Main.TargetNamespace as public.
I did
[assembly: Obfuscation(Feature = "ilmerge custom parameters: /internalize:exclude.file", Exclude = false)]
[assembly: Obfuscation(Feature = "Apply to type Merged.SourceNamespace.Type: type renaming pattern Main.TargetNamespace.Type", Exclude = false)]
But only internalization is disabled (i.e. the first line works): the type Merged.SourceNamespace.Type is public and available in obfuscated assembly.
How can I keep type Merged.SourceNamespace.Type from merged library public and move it to a specified namespace Main.TargetNamespace.Type?
In order to keep Merged.SourceNamespace.Type public, it should be excluded from internalization:
namespace Merged.SourceNamespace
{
[Obfuscation(Feature = "internalization", Exclude = true)]
public class Type
{
// ...
}
}
The corresponding obfuscation settings of Main assembly should look like this:
[assembly: Obfuscation(Feature = "merge with Merged.dll", Exclude = false)]
[assembly: Obfuscation(Feature = "Apply to type Merged.SourceNamespace.Type: type renaming pattern Main.TargetNamespace.Type", Exclude = false)]
P.S. A general piece of advice. While the given recipe allows to achieve the goal, I would strongly suggest to avoid using it in production scenarios. Here is why: the public API of an assembly should be exclusively defined by the source code. Do not rely on a tool such as Eazfuscator.NET to do the API transform for you. Otherwise things quickly become hairy. For example, your colleague or future you may have hard times trying to figure out what's going on with that API and why on earth it depends on obfuscation.
Related
Created a new "WPF Application" .NET 6.0 project
There creating classical Application Settings was easy in project->properties->Settings->"Create or open application settings"
Observed: the project gets a new folder "Properties" which has a yellow Folder icon with an additional black wrench symbol, okay
It contains a new item Settings.settings that can get edited via classical Settings Designer looking like it used to look in .Net 4.8, and a new App.config XML file is getting created automatically in the project's root folder which also looks like it used to in .Net 4.8, okay
Now the same procedure can apparently only be done manually in
a new "Class Library" project being added in the same solution where I would want to use that Properties.Settings / app.config feature pack for storing a DB connection string configurably:
the new sub-project does not seem to have a "Settings" option in the project Properties dialog (as opposed to a .Net4.x would have had)
the new Properties folder and new Settings file can be created successfully there too manually as described in Equivalent to UserSettings / ApplicationSettings in WPF .NET 5, .NET 6 or .Net Core
but doing a "Rebuild solution" gives an
Error CS1069 The type name 'ApplicationSettingsBase' could not be found in the namespace 'System.Configuration'. This type has been forwarded to assembly 'System.Configuration.ConfigurationManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' Consider adding a reference to that assembly. ClassLibrary1 C:\Users\Stefan\source\repos\WpfCorePropertiesSettings\ClassLibrary1\Properties\Settings.Designer.cs 16 Active
as a next step adding NuGet package "System.Configuration.Abstractions" to the Class Library project cures the symptom, "rebuild solution" makes the error disappear.
TLDNR, actual question: is that sequence an acceptable solution or a kludge to avoid?
To me the NuGet package description does not sound as if the package was made for that purpose, and I have not heard the maintainers' names before (which might or might not matter?)
https://github.com/davidwhitney/System.Configuration.Abstractions
TIA
PS:
Maybe I don't understand something...
Why create "Equivalent to UserSettings"?
My configuration is Win10+VS2022. I am creating a WPF .Net6 project. I go to the "Project Properties" menu. In the menu of the project properties tab (column on the left) there is an item Options. When selected, if the settings have not yet been created, there will be a small comment and a link to "Open or create application settings".
Unfortunately, I have Russian localization, so the screenshots are with Russian names.
Addition
But an additional "Class Library" sub-project does not seem to have that Project Properties option in my En/US localization. Does it in yours?
These are the APP settings.
Therefore, they do not make much sense in the library.
But if you need to, you can just copy the class to the library and then set up the links you need.
To do this, type in the application code the line Properties.Settings.Default.Save();. Move the cursor to Settings and press the F12 key.
You will be taken to the source code for the Settings class declaration. This code is generated by a code generator.
After moving to, copy all the source code into a class in another project. After the migration, you may need to add references in the project, fix the namespace and add usings.
As for the parameters in the «Class Library» project, it probably depends on what type this library is.
I have such settings in the «Class Library for WPF».
But in Libraries for Standard - no.
In the meantime I'm happy with a custom "AppSettings.json" approach.
After removing the previously described "classical app.config" approach, and after adding two NuGet packages:
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
... I created a custom Json file on "Class Library" (sub)project level in Visual Studio manually, and set its CopyToOutputDirectory property
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
And added an 'IConfigurationBuilder` part:
using Microsoft.Extensions.Configuration;
namespace Xyz.Data
{
internal class AppSettingsConfig
{
public AppSettingsConfig()
{
IConfigurationBuilder builder = new ConfigurationBuilder();
_ = builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "AppSettings.Movies3Data.json"));
var root = builder.Build();
AttachedDb = root.GetConnectionString("AttachedDb")!;
}
public string AttachedDb { get; init; }
}
}
And then made it a "Jon Skeet singleton"
/// <summary>
/// Singleton as described by Jon Skeet
/// </summary>
/// https://csharpindepth.com/Articles/Singleton
internal sealed class AppSettingsConfigSingleton
{
private static readonly Logger log = LogManager.GetCurrentClassLogger();
private AppSettingsConfigSingleton()
{
log.Trace($"{nameof(AppSettingsConfigSingleton)} ctor is running");
IConfigurationBuilder builder = new ConfigurationBuilder();
_ = builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "AppSettings.Movies3Data.json"));
var root = builder.Build();
AttachedDb = root.GetConnectionString("AttachedDb")!;
}
static AppSettingsConfigSingleton() { }
public string? AttachedDb { get; init; }
public static AppSettingsConfigSingleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly AppSettingsConfigSingleton instance = new();
}
}
And it "works well" by also reading JSON content just having been modified by admins at run-time. (Which would be the Entity Framework Core "localdb" location for the unit-of-work pattern in a multi-UI solution). Thanks again to you too, #EldHasp
Im having trouble referencing classes in xaml from other assemblies.
In the same solution, i have two projects. One called Controls (to hold user controls) and one called DataBinding (holding converters / validation rules). In a control, im attempting reference a validation rule in xaml:
<Binding.ValidationRules>
<databind:Validators.FileExistsRule />
</Binding.ValidationRules>
My project references the project containing my classes. Ive added this declaration at the top of my Control.xaml:
xmlns:databind="clr-namespace:GuiParts.DataBinding;assembly=DataBinding"
However, when i compile, i get an error:
The tag 'Validators.FileExistsRule' does not exist in XML namespace 'clr-namespace:GuiParts.DataBinding;assembly=DataBinding'.
The class definitely exists, i can call it in the code behind with no problems, but not via xaml. If i move the class to the same project, again, i have no problems. Ive seen other questions on here, and have tried the following:
Cleaning and rebuilding all relevant projects
Ensuring all projects are targeting the same version of .Net (4.0, Full Profile)
Removing the 'assembly' definition from the end of the namespace definition.
None of the above has worked. Any suggestions as to where im going wrong?
EDIT
My FileExists Validator:
namespace GuiParts.DataBinding.Validators
{
/// <summary>
/// Validates that the file with the specified name exists
/// </summary>
public class FileExistsRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
ValidationResult res = null;
res = ( ! File.Exists((string)value))
? new ValidationResult(false, "File does not exist")
: new ValidationResult(true, null);
return res;
}
}
}
I can call the following in the code behind without any errors:
new GuiParts.DataBinding.Validators.FileExistsRule();
So ive got my namespaces etc. correct.
Try this:
xmlns:databind="clr-namespace:GuiParts.DataBinding.Validators;assembly=DataBinding"
<Binding.ValidationRules>
<databind:FileExistsRule />
</Binding.ValidationRules>
Is your class in your target assembly public?
Is the field in Validators, public?
Is your namespace GuiParts.DataBinding correct?
While I'm not sure what the issue you're having is, you can alternatively create a friendlier namespace definition for your assembly and CLR namespaces. In fact, I use this technique to group various namespaces into one XML namespace... You do this using the XmlnsPrefixAttribute and the XmlnsDefinitionAttributes.
For example:
[assembly: XmlnsPrefix("http://my.xml.namespace.com/", "databind")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding.Validators")]
Then, when you want to reference the xmlnamespace in your xaml you simply do:
xmlns:databind="http://my.xml.namespace.com/"
Note, I use ReSharper, but I'm sure this also works naturally in Visual Studio. If you don't type in the xmlns import and attempt to use an object in the namespace, then when you go to resolve it it will automatically use the friendlier named namespace with the specified prefix. Also, it's really nice and helps you avoid having a 1:1 relationship between xml namespaces and clr namespaces because you can map multiple clr namespaces into a single xml namespace.
Again, I'm not sure what the issue you're having specifically is, but this will probably fix it and be better than using clr-namespaces and assembly information. Just make sure to come up with something unique so you don't run into xmlns collisions or you'll have to go back to clr/assembly namespacing.
Oh, and one last thing... if you wish to utilize versioning in your xml namespace naming scheme (which you should), don't worry about locking yourself in for backwards compatibility. You can always utilize the XmlnsCompatibleWithAttribute to ensure that code that utilized the old friendly namespace doesn't break if you ever update your external assemblies to map to a newer xml namespace.
For example, if you originally had you're assembly pointing to a 2012 namespace, then switched it to a 2013 namespace because you updated the assembly...
// Previous Assembly version
//[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2012",
// "Xyz.Databinding")]
[assembly: XmlnsCompatibleWith("http://schemas.xyzcorp.com/wpf/2012",
"http://schemas.xyzcorp.com/wpf/2013")]
[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2013",
"Xyz.Databinding")]
i have 2 projects in my solution (main is A.WPF and secondary is B.WPF)
when i'm trying to access variables inside my App.xaml.cs in B.WPF:
filename = ((App)Application.Current).ErrorLogFileName;
i get the following error:
Unable to cast object of type 'A.App' to type 'B.App'.
i also tried the following:
filename = ((B.App)Application.Current).ErrorLogFileName;
but still the same error...
the definition in B.App is:
private string _errorLogFileName = "error log.xml";
public string ErrorLogFileName
{
get { return _errorLogFileName; }
}
please assist...
Looks like you need to do:
filename = ((A.App)Application.Current).ErrorLogFileName;
The error is saying the type is A.App, yet in both cases you are trying to cast to B.App.
There can only be one current application also.
Application.Current refers to the current application. The only way to be allowed to cast the current App to another App-type is when the other App-type is a base class of the current App-type.
Are A.App and B.App siblings or is B.App a base class of A.App?
If you don't want B to have a reference to A (or can't as you want A to reference B and that would cause a circular reference), then you need a common type defined in a third assembly that both A and B reference. In our implementation we tend to have a ConfigurationData type that is in a separate project referenced by both Wpf projects, e.g.
public static class ConfigurationData
{
private static string _errorLogFileName = "error log.xml";
public string ErrorLogFileName
{
get { return _errorLogFileName; }
}
}
Another approach would be to define an Interface for your ErrorLogFileName property in a 3rd assembly that both A and B reference, and then implement that interface on your Wpf Application class - A and B would then both be able to cast to that type. If you wanted your A project to set the values on that at runtime, you could make the ErrorLogFileName a read-write property instead and initialize it in your application startup.
I personally prefer using a separate ConfigurationData type from the Wpf app object for this kind of stuff (ErrorLogFileName etc.) as it can then also be used for code that might execute in a unit test and therefore might not be running under a Wpf application - it also avoids having to do casts all over the place (ConfigurationData.ErrorLogFileName instead of ((IAppConfigurationData)Application.Current).ErrorLogFileName.
BTW, if you have an Application object in both assemblies it sounds like you might have both assemblies configured to build as Output type: Windows Application in your project properties. You should only really have one assembly that is configured as the Windows Application and the rest should be Class Library to avoid confusing numbers of Application classes being generated - only the one in the main EXE (and it's related resources) will get created at runtime.
I have few public properties in App.xaml.cs which is in project A and I want to refer them in my project B. However my project A has a reference to project B, so I cannot add again the reference of project A in project B otherwise it will result in cyclic error. So how can I refer those properties in my class library? I don't want to use reflection :).
As a workaround I have stored those properties in one class in project B (so it can be referred in project A as well as project B) and made those properties to be static and all works fine. However I am still curious to know what if I had stored them in App.xaml.cs? Any options available?
Thanks in advance :)
The App class should expose things that are only relevant to the application project. As soon as you realised that you wanted these things accessable in B.dll they became relevant to more than just the application project and therefore no longer belong in the application project.
Adding a class to B.dll that carries these things as static properties could be a reasonable approach. Another common pattern is to have a single Current static property.
public MyClass
{
private static MyClass _current = new MyClass();
public static MyClass Current { get { return _current; } }
public string SomeInstanceValue { get; set; }
}
Both A and B would access things using the pattern var x = MyClass.Current.SomeInstanceValue. The advantage of this approach is that it allows the Current property getter to determine if a "current" instance is available or not.
You might also want to review the documentation on ApplicationLifeTimeObjects.
When A and B both need something, maybe you should put them in a C project (C as in Common) and then refer to C from both A and B.
I've begun experimenting with dependency injection (in particular, MEF) for one of my projects, which has a number of different extensibility points. I'm starting to get a feel for what I can do with MEF, but I'd like to hear from others who have more experience with the technology. A few specific cases:
My main use case at the moment is exposing various singleton-like services that my extensions make use of. My Framework assembly exposes service interfaces and my Engine assembly contains concrete implementations. This works well, but I may not want to allow all of my extensions to have access to all of my services. Is there a good way within MEF to limit which particular imports I allow a newly instantiated extension to resolve?
This particular application has extension objects that I repeatedly instantiate. I can import multiple types of Controllers and Machines, which are instantiated in different combinations for a Project. I couldn't find a good way to do this with MEF, so I'm doing my own type discovery and instantiation. Is there a good way to do this within MEF or other DI frameworks?
I welcome input on any other things to watch out for or surprising capabilities you've discovered that have changed the way you architect.
Is there a good way within MEF to
limit which particular imports I allow
a newly instantiated extension to
resolve?
Load the extension code in a separate container, and make sure that the restricted parts are not available in that container. Let's simplify the situation to these classes to construct an example:
[Export]
public class MyExtension
{
[Import]
public PublicService Service { get; set; }
}
[Export]
public class PublicService
{
}
[Export]
public class InternalService
{
}
[Export]
public class Program
{
[Import]
public MyExtension Extension { get; set; }
[Import]
public PublicService Service1 { get; set; }
[Import]
public InternalService Service2 { get; set; }
}
The goal is to allow MyExtension to import PublicService, but not InternalService. Internal code like Program should be able to import anything. You can achieve that like this:
var publicCatalog = new TypeCatalog(typeof(PublicService), typeof(MyExtension));
var publicContainer = new CompositionContainer(publicCatalog);
var internalCatalog = new TypeCatalog(typeof(Program), typeof(InternalService));
var internalContainer =
new CompositionContainer(internalCatalog, publicContainer);
var program = internalContainer.GetExport<Program>();
This code will not throw a composition exception. If you now change the import on MyExtension to the forbidden InternalService, you will get a composition exception as desired.
A side effect of this set-up is that PublicService cannot import any private services either, just like MyExtension. This kind of makes sense, because otherwise nothing would stop PublicService from exposing a private service via a property.
I have used TypeCatalog for the example, but in practice you should probably use something like the FilteredCatalog sample.
This particular application has
extension objects that I repeatedly
instantiate. I can import multiple
types of Controllers and Machines,
which are instantiated in different
combinations for a Project. I couldn't
find a good way to do this with MEF,
so I'm doing my own type discovery and
instantiation. Is there a good way to
do this within MEF or other DI
frameworks?
You might just be after the PartCreationPolicy attribute, which controls whether a part is shared (as in, created only once per container) or instantiated multiple times for each import. You can also specify the RequiredCreationPolicy parameter in an import attribute.
If that doesn't solve your problem, take a look at the PartCreator sample in the MEF sources (though note that it will probably soon be renamed to ExportFactory, it already has been in the silverlight edition of MEF).