c# Runtime lose reference - c#

I separated the Data Access Layer in my project to apply the different validations rules by countries.
For example i have an interface "IVoucherService"
public interface IVoucherService
{
void Foo();
}
which implement in many Project
namespace VoucherHU
{
[ApiLocalization(ApiLocalization.HU)]
public class VoucherService : IVoucherService
{
public void Foo(){}
}
}
namespace Voucher
{
[ApiLocalization(ApiLocalization.US)]
[ApiLocalization(ApiLocalization.DEFAULT)]
public class VoucherService : IVoucherService
{
public void Foo(){}
}
}
The following code determine which class has to be used
private static Type GetServiceImplementedType<TService>() where TService : class
{
Type serviceType = typeof(TService);
IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(p => !p.IsInterface && serviceType.IsAssignableFrom(p));
Type serviceImplementedClass = null;
foreach (Type type in types)
{
ApiLocalizationAttribute[] attributes = (ApiLocalizationAttribute[])type.GetCustomAttributes(typeof(ApiLocalizationAttribute), true);
if (attributes != null && attributes.Count() > 0 && attributes.Any(a => a.Localization == Localization))
{
serviceImplementedClass = type;
break;
}
}
return serviceImplementedClass;
}
I register these services in the Global.asax -> Application_Start method.
After publish it works fine, the services starts correctly, but if nobody use the website, the IIS (maybe to save resource) pauses the service until the next request. When the API try to re register the services it could not find any class which implements the IVoucherService interface.
I checked it manually in the watch and i got the following error
Services.Voucher.VoucherService error CS0234: The type or namespace
name 'Services' does not exist in the namespace (are you missing an
assembly reference?)
and i got the same error for the other classes which implements this interface.
Any idea why i loose the refference to these files (Services.dll, ServicesHU.dll)?

I figured it out.
Somehow the AppComaing.CurrentDomain.GetAssemblies()no longer contains the assemly informations of the DLL files.
The solution was to refresh the referrenced assemblies on each time when Global.asax -> Application_Start method called.
To refresh the referrenced assemblies use this method
System.Web.Compilation.BuildManager.GetReferencedAssemblies();

Related

Create instance of an object in a new app domain in azure function throws a FileNotFoundException

I need to run some code in a new app domain. So I am trying to create an instance of my object in the new app domain... Here te code I am using:
public static class Program
{
private static ITemplateEngineProvider _templateEngineProvider;
static Program()
{
AppDomain ad = AppDomain.CreateDomain("New domain");
ObjectHandle handle = ad.CreateInstance(
assemblyName: typeof(RazorTemplateEngineProvider).Assembly.FullName,
typeName: "RazorTemplateEngineProvider"
//ignoreCase: false,
//bindingAttr: BindingFlags.CreateInstance,
//binder: null,
//args: new object[] { new string[] { templatePath, layoutPath } },
//culture: CultureInfo.InvariantCulture,
//activationAttributes: null
);
_templateEngineProvider = (RazorTemplateEngineProvider)handle.Unwrap();
}
}
RazorTemplateEngineProvider is a custom public class that has a public constructor. It has been implemented in a class library (MyCustomLib.dll) I referenced inside the azure function. The class implements an interfaces defined in another class library (IMyCustomLib.dll) referenced only by the previous class library, not by azure function.
At the moment there is no code inside the RazorTemplateEngineProvider class:
public class RazorTemplateEngineProvider : MarshalByRefObject, ITemplateEngineProvider
{
public RazorTemplateEngineProvider()
{ }
}
When I try to do ad.CreateInstance a FileNotFoundException has been thrown:
Could not load file or assembly 'MyCustomLib.dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...' or one of its dependencies. The system cannot find the file specified.
But the file exists and it should be already correctly loaded... Infact if I run this 'query'
IEnumerable<string> loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic && !a.FullName.Contains("Version=0.0.0.0") && File.Exists(a.Location) && !a.Location.Contains("CompiledRazorTemplates.Dynamic") && a.FullName.Contains("My"))
.Select(f => f.FullName)
.ToArray();
I see both my dll. So, why do I get that error?
Thank you
UPDATE
I think problem is azure because I copied and pasted my code in a console application and thereit works.
UPDATE
I am watching the fusionlong and it seem it is trying to load the assembly from a "wrong" path: file:///C:/Users/csimb/AppData/Local/Azure.Functions.Cli/1.0.12/MyCustomLib.dll.. I expected the path was the bin folder...

checking if any class has a custom attribute defined in assemby where both custom attribute and class are loaded dynamically

I am working in wcf services where services are host on random ports dynamically. Service contract and service behavior assembly are loaded dynamically and all types are scanned to match service name and its version.
Same service can be running on different versions.To distinguish the versions of service we have created a custom ServiceIdentifierAttribute attribute.
public class ServiceIdentifierAttribute : Attribute
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _version;
public string Version
{
get { return _version; }
set { _version = value; }
}
}
Service contract and its behavior class are decorated with SerivceIdentifierAttribute.
[ServiceContract(Name = "ABCServicesV0.0.0.0")]
[ServiceIdentifierAttribute(Name = "ABCServicesV0.0.0.0", Version = "V0.0.0.0")]
public interface IABCService
{
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, Name = "ABCServicesV0.0.0.0")]
public class ABCService : IABCService
{}
Service contract,attribute are defined in one assembly and Service implementation in another. We have a GenericSericeHost console application which dynamically host services by loading both assemblies. We need to search all types and get Service contract type from assembly.
private static bool SeachForServiceContractAttribute(Type type, String serviceIdentifier)
{
if (type.IsDefined(typeof(ServiceContractAttribute), false))
{
var attributeTypes = type.GetCustomAttributes();
foreach (var attributeType in attributeTypes)
{
try
{
ServiceContractAttribute attribute = (ServiceContractAttribute)attributeType;
if (attribute != null && !string.IsNullOrEmpty(attribute.Name) && attribute.Name.Equals(serviceIdentifier))
return true;
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
}
return false;
}
GenericServiceHost has a reference to ServiceContract assembly. At runtime
ServiceContractAttribute attribute = (ServiceContractAttribute)attributeType;
is throwing error Invalid cast exception. As two versions of the ServiceContractAttribute are loaded at runtime. One is loaded dynamically and another one by GenerciServiceHost reference. We can't remove the service reference as it will result into ServiceContractAttribute not defined complication error.
All different service implementations will have a different assembly and we don't want to add reference to all assembly from genereicservicehost as it will lead to rebuilding genericservicehost when any of the service behavior changes. We want GenericServiceHost to run all the time.
How we can make this to work by cast from assembly loaded type to assembly loaded type
ServiceContractAttribute attribute = (ServiceContractAttribute)attributeType;
Any pointer ?
Your architecture is flawed. It seems you have declared ServiceContractAttribute multiple times, and then yes, the cast won't ever work, since each declaration produces a distinct type.
You have to decompose ServiceContractAttribute into a separate assembly defining the common API between the host application and service assemblies, and share it across all services.

Where to place AutoMapper map registration in referenced dll

This is my first AutoMapper project and may be obvious to some but the tutorials and examples are not clicking with me. I am trying to understand where and to a certain degree how to register(I think I want profiles) my maps for use. There are plenty of MVC examples saying to use the global asax and this makes sense but what is the equivalent in a library project?
In my sandbox I have a winform app and a core library. The winform app calls methods made available by the library and it is one of these library methods that makes use of automapper.
So for some background here is my map:
(and to be clear the mapping is in the SAME core library project)
public class Raw_Full_Map
{
public Raw_Full_Map()
{
Mapper.CreateMap<IEnumerable<RawData>, FullData>()
.ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
//this is clearly just a snip to show it's a basic map
}
}
This is the core library method being called: (note it is a static..which means I won't have a constructor...if this is the problem am I to understand then that AutoMapper can't be utilized by static helper classes...that doesn't make sense....so likely I'm just not doing it right.
public static class RawDataProcessing
{
public static FullData HTMLDataScrape(string htmlScrape)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(htmlScrape);
var list = Recurse(doc.DocumentNode);
//HTML agility stuff that turns my html doc into a List<RawData> object
return Mapper.Map<FullData>(list);
}
My test harness calls it like this:
var _data = RawDataProcessing.HTMLDataScrape(rawHTML);
This of course errors because the map isn't "registered".
If I do this in the test harness:
var x = new RawData_FullData();
var _data = RawDataProcessing.HTMLDataScrape(rawHTML);
Then everything works as my map get's registered albeit I think in a really bogus way...but it does work.
So the question is how do I register my mapping in the core library project...so that ANY method can use it...there isn't really an equivalent global.asax in a dll is there?
Thank you for helping me connect the missing pieces.
Put it in the static constructor of either the source or the target type of the mapping.
public class FullData
{
static FullData()
{
Mapper.CreateMap<IEnumerable<RawData>, FullData>()
.ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
}
}
The static constructor will automatically get called the first time you try to use the type FullData for anything (for example a mapping).
You can use PreApplicationStartMethod for any class and it's method in your class library which will be referenced from your startup project if you want automatically to call this on startup. And then you can register all your mappings in that method. By the way, I suggest to use AddProfile for registering all mappings.
[assembly: PreApplicationStartMethod(typeof(MyClassLibrary.Startup), "Start")]
namespace MyClassLibrary
{
public class Startup
{
// Automatically will work on startup
public static void Start()
{
Mapper.Initialize(cfg =>
{
Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
{
cfg.AddProfile(Activator.CreateInstance(match) as Profile);
});
});
}
}
}
You just need to create new classes which derived from Profile class and then override it's Configure() method:
...
public class FooMapperProfile:Profile
{
protected override void Configure()
{
Mapper.CreateMap<OtherFoo, Foo>()
.ForMember(...
... // so on
}
}
public class AnotherFooMapperProfile:Profile
{
protected override void Configure()
{
Mapper.CreateMap<OtherFoo, AnotherFoo>()
.ForMember(...
... // so on;
}
}
...
// and so on
Additional information:
If you have seen I have initialized all mappings with that code:
Mapper.Initialize(cfg =>
{
Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
{
cfg.AddProfile(Activator.CreateInstance(match) as Profile);
});
});
It will automatically find all types derived from Profile and will add all profiles after createing their new instances.
Update1:
As #Scott Chamberlain commented, PreApplicationStartMethod only works for ASP.NET applications. This would not work with a desktop app. If you are working with Wpf, then you can use Application.OnStartup method. Or just call Start.Startup (); in load event.
Update2:
FindAllDerivedTypes extension method:
public static class AssemblyExtensions
{
public static List<Type> FindAllDerivedTypes<T>(this Assembly assembly)
{
var derivedType = typeof(T);
return assembly.GetTypes()
.Where(t => t != derivedType && derivedType.IsAssignableFrom(t))
.ToList();
}
}

Interface cannot be constructed

I have gotten myself into an interesting situation, and i am confused since i think i am doing all the right stuff here... I am getting the following error:
The current type, Services.Interfaces.IKenticoService, is an interface and cannot be constructed. Are you missing a type mapping?
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The current type, Services.Interfaces.IKenticoService, is an interface and cannot be constructed. Are you missing a type mapping?
Source Error:
Line 113: throw new InvalidOperationException("Container on Global Application Class is Null. Cannot perform BuildUp.");
Line 114:
Line 115: container.BuildUp(this as T);
Line 116: }
Line 117:
Source File: c:\PROJECTS\CMS\CurentSprint\currentsprint\Source\WebProject\App_Code\Global\BasePage.cs Line: 115
and the error is generated by base page:
protected override void OnPreInit(EventArgs e)
{
InjectDependencies();
base.OnPreInit(e);
}
/// <summary>
/// This method is used to inject any controller related dependencies from
/// our existing web page.
/// </summary>
protected virtual void InjectDependencies()
{
HttpContext context = HttpContext.Current;
if (context == null)
return;
IContainerAccessor accessor = context.ApplicationInstance as IContainerAccessor;
if (accessor == null)
return;
IUnityContainer container = accessor.Container;
if (container == null)
throw new InvalidOperationException("Container on Global Application Class is Null. Cannot perform BuildUp.");
container.BuildUp(this as T);
}
I have the mappings in place:
namespace Core.DI
{
public static class UnityHelper
{
public static void ConfigureContainer(IUnityContainer container)
{
container.RegisterType<IPersonRegistrationService, PersonRegistrationService>();
container.RegisterType<ILoginService, LoginService>();
container.RegisterType<IKenticoCMSOfferService, KenticoCMSOfferService>();
container.RegisterType<IKenticoService, KenticoService>();
and then i have some other...
}
}
}
This method is called in side global Application Start method:
public void Application_Start(object sender, EventArgs e)
{
// Azure Application start init
AzureInit.Current.ApplicationStartInit();
CMSAppBase.CMSApplicationStart();
//CustomCode: Added for DI (Unity block)
try
{
//CustomCode: Create the unity container.
Container = new UnityContainer();
UnityHelper.ConfigureContainer(Container);
//mappings
EntityMapper.MapEntities();
}
catch (Exception ex)
{
//TODO: add call to error logger.
}
}
My KenticoService class is setup properly as well:
namespace BusinessLogic.Services
{
public class KenticoService : IKenticoService
{
#region User API Calls
public void HandleCmsUser(Person person, string userName)
{
...
}
public void HandleCmsUser(Person person, string userName, string oldUserName)
{
...
}
#endregion
}
}
Now the kentico service methods are called inside LoginService and PersonRegistrationService only. So in both the classes i have:
[Dependency]
public IKenticoService KenticoServiceInstance { get; set; }
Now we have two sites, our custom MVC solution and a CMS site. The services referenced above are in the projects that are inside our MVC solution. For CMS use, we copy the dlls over to the CMS solution. The MVC solution compiles and runs great. The CMS site is throwing this error and i have double checked that the correct dlls are being referenced here. Are you seeing something here that i may be missing?

MEF 'The export is not assignable to type' error

I have just started using MEF and have hit on an early problem.
I have an interface called DataService:
namespace DataAccess
{
interface IDataService
{
string Name { get; }
string Description { get;}
List<String> GetPeople();
}
}
There are 2 implementations of this interface, one for SQL Server and one for Oracle.
Below is the Oracle implementation, SQL Server implementation is exactly the same.
namespace DataAccess
{
[Export(typeof(IDataService))]
[ExportMetadata("Name","Oracle")]
[ExportMetadata("Description","Oracle Data Service")]
public class Oracle : IDataService
{
#region IDataService Members
public string Name
{
get { return "Oracle"; }
}
public string Description
{
get { return "Provides data access to Oracle database"; }
}
public List<string> GetPeople()
{
return new List<String>() { "Oracle boo", "Oracle boo1" };
}
#endregion
}
}
The name and description properties are now defunct as I have replaced these with metadata. As you can see, they are very simple objects, I wanted to make sure I could get this to work before I started doing the hard work.
This is the code I am using to discover the assemblies:
private static CompositionContainer _container;
private const string ASSEMBLY_PATTERN = "*.dll";
private AggregateCatalog _catalog;
[ImportMany]
IEnumerable<DataAccess.IDataService> services { get; set; }
private void button3_Click(object sender, EventArgs e)
{
_catalog = new AggregateCatalog(
new DirectoryCatalog(txtLibPath.Text, ASSEMBLY_PATTERN),
new AssemblyCatalog(Assembly.GetExecutingAssembly()));
_container = new CompositionContainer(_catalog);
_container.ComposeParts(this);
MessageBox.Show(services.Count().ToString());
}
This is the error that is produced:
The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) The export 'DataAccess.Oracle (ContractName="DataAccess.IDataService")' is not assignable to type 'DataAccess.IDataService'.
Resulting in: Cannot set import 'MEFTest.Form1.services (ContractName="DataAccess.IDataService")' on part 'MEFTest.Form1'.
Element: MEFTest.Form1.services (ContractName="DataAccess.IDataService") --> MEFTest.Form1
It doesn't seem to make any sense that it can't assign to the interface that it was designed for!
Once this problem is solved, my next issue is how to pick one and get an instance of it...
It looks like two different versions of your contract assembly (the one with DataAccess.IDataService) are getting loaded. One is probably from your executable path and the other from your plugin path. I touch on this issue a bit in my blog post on How to Debug and Diagnose MEF Failures, and the MSDN page on Best Practices for Assembly Loading goes into more detail.
Yet another cause:
Code:
interface IMyService
{
}
[Export(typeof(IMyService))]
class MyService
{
}
Message:
The export 'IMyService' is not assignable to type 'IMyService'.
Cause:
The MyService class does not implement the IMyService interface.
For me this had a very simple fix.
Here's a link! that explains the root cause.
In my case, I locked my Assembly version down, but my file version travels. My nuget package ID matches my assembly file version.
Final result is that I can build continuously, create new nugets, and not have this MEF inteface problem.
I must tell that I had such an error in completely idiotic context. Accidentally, I misplaced export directive and put it not on class but on a function inside class:
interface MyInterface
{
void MyFunction();
}
public class MyClass : MyInterface
{
[Export(typeof(MyInterface))]
void MyFunction() { }
}
Surprisingly, the code compiled very fine without any warnings. But then I ve spent hours trying to figure out why MEF fails on my silly misprint!

Categories