Missing attributes on dynamically loaded class - c#

I am using Structure Map to load plugins from a child directory.
Both the main app and the plugin reference the FileHelpers dll. FileHelpers has attributes that you put on a class to define what the record is delimited by. These are defined in my plugin. eg.
[Delimited('\t')]
public class Test {
public string name;
}
The FileHelpers utitlity is run from the main app using the class definitions provided by the plugins. If I put the plugin dll in a directory underneath the main application then I get an issue with the FileHelpers library complaining that the attribute cannot be found, however if it is placed next to the main library (same folder), then it works fine.
I have placed some further debug statements into my code and have found that if
var type = typeof(Test);
var attributes = type.GetCustomAttributes(true);
is used and not the specific (the one FileHelpers is using)
var attributes = type.GetCustomAttributes(typeof(DelimitedAttribute), true);
then it finds the custom attributes without any troubles.
I thought this may have been a SM thing but have tried MEF and by doing it using Assembly.Load() and the same thing happens.

I think you are running into the issue described here.
According to the blog post linked in the answer, it looks like the plugin dll would need to be strongly named and fully trusted, otherwise GetCustomAttributes will filter out the DelimitedAttribute. You could try to add the AllowPartiallyTrustedCallers attribute to the plugin assembly.

Related

How do I use a type with the same name of another type in another referenced assembly?

I have the absurd situation (don't blame me, it is third party software) where I need to have two references (Erp.Contracts.BO.Quote and Erp.Contracts.BO.SalesOrder), but the type Erp.Tablesets.QuoteQtyRow is defined in both assemblies!
How do I use them in code?
void Absurdity()
{
Erp.Tablesets.QuoteQtyRow qqr_Quote = null; //<-- my intention is to use the one from the quote assembly here.
Erp.Tablesets.QuoteQtyRow qqr_SO = null; //<-- my intention is to use the one from the sales order assembly here.
}
The compiler throws an error. Namely: "The type 'Erp.Tablesets.QuoteQtyRow' exists in both assemblies."
EDIT: LIMITATIONS:
I do not have the flexibility of using an extern alias as provided in this answer Class with same name in two assemblies (intentionally). I am limited by the environment supplied by the third party software. I essentially need a way to make the distinction within the body of a method.
I understand I can avoid this problem altogether by using the dynamic keyword, but I am looking for a possible strongly typed solution.
There may not be a solution, but I want to exhaust all my resources before I give up on the problem.
Epicor ERP uses a tool to put together tables from the DB into datasets, and then on into Business objects. This business object is described in the contract assembly, but as you have found when you use two business object that references the same table you run into problems. This is more commonly an issue with the SerialNumber tables.
I understand from your notes that you are providing method body code in a Method Directive or data Directive within the Epicor ERP application. This is entered on the client and stored in the database but generates code on the server in the Deployment\Server\BPM\Sources\BO folder and is compiled to the Deployment\Server\Customization\BO folder.
There is no way to specify an alias for the referenced DLL in the "Execute Custom Code" workflow item of the BPM designer. The fix is requested in SCR 148549. There is no project file for you to edit, and even if there was every time the BPM was enabled and disabled it would be regenerated.
However, if you use the "Invoke External Method" workflow item, then you can build your own dll and put it in the Deployment\Server\Customization\Externals folder. To do that:
Click Actions > Create Programming Interfaces for your method in Method Directive Maintenance for your BPM and copy the code.
Create a new Class library project in Visual Studio
Paste the copied code into the .cs file
Add Assemblies - Framework references:
System.Data.Entity
System.ServiceModel
System.Transactions
Add file references to
Bin\Epicor.ServiceModel.dll
Assemblies\Epicor.Ice.dll
Assemblies\Epicor.System.dll
Assemblies\Ice.Data.Model.dll
Assemblies\Erp.Data.910100.dll
And add a reference for the BPM's BO i.e.
Assemblies\Erp.Contracts.BO.Quote.dll
Ensure all the references have Copy Local set to false.
Inherit from Ice.ContextBoundBase<Erp.ErpContext>
Add a constructor that takes a context public MyQuote (Erp.ErpContext ctx) : base(ctx){ }
You can't quite copy and paste the "Execute Custom Code" body in as you won't have access to the tt row variables, these are all in the ds.

How to add custom build data to a .net dll

I am working on a winforms application and we deploy the dlls on a DEV server many times a day. We want to be able to find out who built the dll.
I have tried adding System.Security.Principal.WindowsIdentity.GetCurrent().Name to assembly info but it takes only const.
Is there some way to embed username into the dll during build ?
StackOverflow and Coding Horror have examples of creating custom assembly attributes. Based on those examples, you could create something like:
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyBuildSystem : System.Attribute
{
private string _strBuildSystemName;
public AssemblyBuildSystem(string buildSystemName)
{
_strBuildSystemName = buildSystemName;
}
public BuildSystemName { get { return _strBuildSystemName; } }
}
That will give you a custom "AssemblyBuildSystemName" attribute that you can examine via reflection. The problem will be making sure that it's correct at each build, since an attribute can only take constant parameters.
You can add the attribute to the assembly as normal:
[Assembly: AssemblyBuildSystemName("Bob's Development Machine")]
The downside is that you don't want this to be source-controlled, so it probably should reside in a non-source-controlled .cs file specific to each developer's machine. You'll have to rely on each developer to create the file, make sure it's not source-controlled, and make sure that the content is accurate.
You might be able to modify the project target to pass the hostname in as a conditional compilation constant, or to create and add that file as a pre-build step, but at some point it will become easier to go with a build server or modify your deployment process.

Early Binding of a C# COM library in VBA

Although this is a long question the coding and testing part should be really easy to reproduce.
I have created two separate Class Libraries in C# and I think I am running into a name collision problem caused by existing registry keys from my previous projects and trials.
Here are my two classes:
using System;
using System.Runtime.InteropServices;
namespace Test
{
[InterfaceType(ComInterfaceType.InterfaceIsDual),
Guid("ED5D264B-1D80-4A5D-9C14-8297D90B7037")]
public interface ITest
{
// body
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("8B261B92-8EC5-4CDC-A551-67DEB42137FF")]
[ProgId("Test.TestClass")]
public class TestClass : ITest
{
// body
}
}
and
using System;
using System.Runtime.InteropServices;
using ADODB;
namespace Test
{
[InterfaceType(ComInterfaceType.InterfaceIsDual),
Guid("ED5D264B-1D80-4A5D-9C14-8297D90B7037")]
public interface IConnection
{
// body
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("8B261B92-8EC5-4CDC-A551-67DEB42137FF")]
[ProgId("Test.Connection")]
public class Connection : IConnection
{
// body
}
}
I have Exposed .Net Components to COM like this:
In order to access the assemblies from Excel I have added the ADODB references to the assembly, ticked make assembly COM visible and register for com interop. Also, I've added references to each *.tlb file(2 files for two projects) so I can access them using an early binding and use VBA Intellisense.
I have followed the same procedure on another machine and I can use early binding using the Connection as class.
I am thinking there are some old registry keys I haven't deleted on my original machine which will not allow me to use Connection as the class name in VBE. I've manually scanned my registry and deleted everything I could think of related to my project.
I have also deleted the project entirely and used a 3rd party software to scan registry for missing dlls however that didn't help:/
Removed all previously registered GUIDs and applied new ones each time I created a new Project (just in case)
Created new projects using different namespaces and class names (using ADODB;) I haven't been able to use early binding yet like this Test.Connection therefore I am assuming I have a name collision problem. I am suspecting the name class Connection to be causing it although I am not 100% sure.
The Test.TestClass namespace in VBA:
I can declare and use instances of the TestClass type in two ways using early binding:
Dim x as Test.TestClass
Dim x as TestClass
Now going into VBE Object Explorer F2 the TestClass is properly displayed in comparison to other libraries and general idea of using COMs.
However, when I want to use the Test.Connection library I am unable to use early binding following the same pattern as TestClass because the generated *.tlb file automatically changes(renames) the ProgId's. So, instead I have to bind it like this
Dim x As Test.Test_Connection
Dim x As Test_Connection
and the Object Explorer displays the names using _ (underscores) and not . (dots), which is easy to explain why this happens - keep reading :)
As it stands I am sure it is not the VBE environment that changes the names to avoid collisions. It is the VS' *.tlb generator.
I went to the assembly folder and opened both *.tlb files in Notepad++. I can clearly see that the *.tlb for the Test.Connection library already includes the names with the _s unlike the Test.TestClass which has .s
I have tried to manually edit the *.tlb file but as its a mixed binary file it takes some effect but also causes Excel to stop responding in some weird ways so I have to avoid this method.
I think I have explained well what the problem is and where it comes from. Now my question is: Are there any attributes to use in C# code to tell the *.tlb generator not to override my ProdIds? Are there any alternative ways of manipulating *.tlb files? Is this issue a name collision and is it avoidable without changing the name of Connection class?
I'm sorry for such long question but I have been digging and digging for almost a week now and I still cant solve this.
Note: In VBA ( or VBE Object Explorer ) using IntelliSense ctrl+space it does not seem that either Connection or Recordset have been used. Since they are not already reserved in the VBE environment I recon it has to do with my library itself.
As a reference to why this issue has been raised here, please see VBA equivalent to C# using or VB.NET imports creating aliases
Thank you very much for your time!
Do avoid focusing on the ProgId. You are not actually using it, the dialogs that you made a screenshot of show the actual class names, not the ProgId.
Getting the class name renamed to "Test_Connection" is normal behavior for the type library exporter. It will do so whenever it detects a conflict with another interface or class name that has the same name. You are certainly increasing the likelihood of this happening by also having a dependency on ADODB, it also has a Connection class. A very trivial solution is to simply rename your own type.
Your code snippet cannot reproduce this problem. But of course it is incomplete, we can't see what you are really doing in the code. You'll bring in the dependency on ADODB if any of your public methods use a type from this type library. Also note that there are non-zero odds that this will happen by accident. You might have written a method that intended to use your own Connection type but the compiler resolved it to the ADODB type.
An essential tool to debug this is Oleview.exe, run it from the Visual Studio Command Prompt. First create the type library for your C# assembly with Tlbexp.exe. Then use File + View Typelib, you'll see the content of your type library expressed in the IDL syntax. You'll have little trouble recognizing the mapping of your C# types to the IDL declarations.
Pay attention to the importlib directives at the top of the file. They should look like this:
// TLib : // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
There should only be those two. The first one imports the .NET types, defining _Object. The second one imports standard COM types, like IDispatch. If you see additional ones here then you increase the odds of a name collision.
This IDL also gives you a way to solve the problem, in case it is unsolvable, you can edit it to name the types the way you want them. Save it to a .idl file. And compile it with midl.exe /tlb to generate a type library with your preferred names. Do note that this is not something you want to have to do often.

Equivalent to PHP's include in C#

What is the equivalent command to PHP's include() in C# ?
For example, PHP's include is used as so : include("ex.php");
Can I do the same in C#?
If you mean in ASP.Net using C# you can create a user control (.ascx) and add it in your .aspx page.
If you are doing MVC you can create a partial view.
The closest thing I can think of would be after creating an ascx user control named "MyUserControl"
in your page_load or pre_render :
MyUserControl cont = new MyUserControl();
this.Controls.Add(cont);
There is no such thing in C#. It's not a scripting language, so including a block of script wouldn't make sense.
What are you trying to accomplish? There are ways to do similar things in C#.
There is no direct equivalent. You use references to "link" with other CLR assemblies (access their type information), and the using directive to import namespaces.
For example, the FontCollection class is in the System.Drawing.dll assembly, and the System.Drawing.Text namespace. So you would add System.Drawing as a reference, and add the line:
using System.Drawing.Text;
I'm not sure, if this is what you want to do. But just for the case, maybe you have a look at:
<%
Response.WriteFile( "YourFile.whatever" )
%>
In addition to previous answers mentioning the using Directive and adding references to assemblies to your project (or at command line when compiling) there is a way to load other compiled .NET assemblies at runtime.
Assembly.Load will load an assembly (compiled c# file/.dll) into memory, allowing you to find and use types within that assembly. This can be used when building a plugin architecture. You publish an assembly with an interface for a plugin contract. Plugin makers can link to that that assembly and implement your interface. Your application can then load plugin assemblies, check for any types implementing your plugin interface and load and use those types into your application.
The only thing comparable in C# is using, which imports namespaces defined in assemblies referenced from the project. You cannot "include" a file in the sense that you dump the content right into your code.
For example, if your project references the System.Xml assembly, then the following code would allow you to access all of the classes in that namespace without fully qualifying their names:
using System.Xml;
This will let you use the type System.Xml.XmlDocument, for example, by specifying it as XmlDocument instead of its full type name System.Xml.XmlDocument.
There is no such thing in C#.
You're going to want to create an instance of a C# class and use that to invoke methods/attributes from other 'packages' (C# classes).
You can also use a using direction to be able to references assemblies from other projects.
Using is vaguely similar. It references another class that can then be used from that file, but it doesn't include the contents of that file directly inline.
using system;
(right at the beginning of a file)
Use this for c#
#RenderPage("header.cshtml")
This is taken from here:
http://www.w3schools.com/aspnet/showfile_c.asp?filename=try_webpages_cs_002
Although I know this post is old, but people stubming to this post can refer to it.
Well, here is what I did ... not sure if this is the right way, but it works...
In the .ascx File, specify a div as container to received the contents of included file.
<div id="containerForSomeMarkup" runat="server"></div>
In the .ascx.cs file, initialise this in the onInit() method or other methods as required...
containerForSomeMarkup.InnerHtml = File.ReadAllText("Full path of file to be included");
Coming from PHP world, for me, this approach helps keep the markup organized and intelligble...
The using keyword is what you're looking for.

How can I find out all the references for the current project and tell which ones derive from a particular base class in C#?

Due to politics at the very large finanial institution for which I work, I am not able to use Castle Project's ActiveRecord implementation, but like the pattern so much I have implemented it myself. This is complete, and now management is looking for a GUI tool to browse all the activerecord classes, search for instances and manage data.
To this end, I'm building a "browser" that iterates through all the classes in a referenced project, and if they are derived from a partiular base class ("ActiveInstanceBase"), make them available for inspection and modification in an ASP.net datagrid.
The first step for me is to figure out how to iterate through all the references in the current project (developers using this tool will add their dlls to the project as references) and identify the ActiveInstance classes in order to fill a dropdown full of types to inspect.
How do I get a list of all the references for a current project? Google is not turning anything up for me on the first page of results for a number of queries. I'm getting a lot of stuff about writing Visual Studio addins, but nothing for runtime inspection.
How do I determine the base class of a derived type at runtime if the base class takes a Type parameter?
if (t.IsSubclassOf(typeof(ActiveInstance.ActiveInstanceBase)))
{}
Isn't the proper syntax, and I can't know t at runtime.
I'm also forced to use IE6, so pardon if this post is not very well-formatted. Thanks very much in advance!
1) How to get the assemblies referenced in your project
Assembly ourAssembly = Assembly.GetEntryAssembly();
AssemblyName[] refs = ourAssembly.GetReferencedAssemblies();
2) Use Type.IsSubclassOf() or Type.GetInterface()
Type theType = typeof(ActiveInstance.ActiveInstanceBase<>);
foreach(Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(theType))
{ ... }
}
Those should work for you...
if you have political rules against downloading third party software, this may not work, but I use .net Reflector. It will give you the references and decompile the code for reviewing.
http://www.red-gate.com/products/reflector/

Categories