I have started using ServiceStack today. So, let me know if I am doing something completely wrong.
I am trying to inject Db into my Service class, for which I am using this code
[TestFixture]
public class UserServiceTests
{
private UserService service;
[SetUp]
public void SetUp()
{
var appHost = new BasicAppHost();
var dbFactory = new OrmLiteConnectionFactory(":memory:", false, SqliteDialect.Provider);
appHost.Container.Register<IDbConnectionFactory>(dbFactory);
service = new UserService();
service.SetAppHost(appHost);
}
[Test]
public void Calling_post_method_with_valid_User_saves_it_in_database()
{
var User = new User { Name = "Acme Limited" };
var id = service.Post(User);
Assert.AreEqual(1, id);
}
}
There are two problems:
1) I am getting exception:
Could not load file or assembly 'System.Data.SQLite, Version=1.0.82.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Is there an easy way to fix this? And do we really need SQLite for testing, is there is Fake object available?
2) The compiler is warning about - service.SetAppHost(appHost). SetAppHost is depricated.
How can I inject Db into my service class without using SetAppHost?
Any ideas?
Sounds like you have a platform incompatibility issue with Sqlite, make sure you're using the right Sqlite package for your machine.
As for service.SetAppHost(appHost) the deprecated message says to use service.SetResolver(appHost) as IAppHost also implements IResolver, so use that.
Related
I am learning about ASP.NET Core 3 and have built a basic application. I am looking run integration tests to assert calls to the controllers read/write from the database correctly. To avoid having to rely on the actual database I am looking at using EF Core's in-memory database. I have been following this article as my main guide.
The problem I have is that I am struggling to ensure each separate integration test uses a fresh database context.
Initially, I encountered errors calling my database seed method more than once (the second and subsequent calls failed to add a duplicate primary key - essentially it was using the same context).
From looking at various blogs, tutorial and other questions here, I worked around this by instantiating the in-memory database with a unique name (using Guid.NewGuid()). This should have solved my problem. However, this gave me a different issue. The database seed method was correctly called at each test initialisation, however when I then called a controller action the dependency injection instantiated a new database context, meaning that no seed data was present!
I seem to be going in circles either only being able to call seed data once, and only being able to have a single test, or having more than one test but with no seed data!
I have experimented with the scope lifetimes for the DbContext service, setting this to transient/scoped/singleton, but with seemingly no difference in results.
The only way I have managed to get this to work is to add a call to db.Database.EnsureDeleted() before the call to db.Database.EnsureCreated() in the seed method, but this seems like a massive hack and doesn't feel right.
Posted below is my utilities class to set up the in-memory database for the tests, and a test class. Hopefully this is sufficient, as I feel this post is long enough as it is, but the actual controller / startup class can be posted if necessary (though they are fairly vanilla).
Any help much appreciated.
Utilities class to set up the in-memory database
using CompetitionStats.Entities;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
namespace CompetitionStatsUnitTests
{
class Utilities
{
internal class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the app's ApplicationDbContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<CompetitionStatsContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add ApplicationDbContext using an in-memory database for testing.
services.AddDbContext<CompetitionStatsContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<CompetitionStatsContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
db.Database.EnsureDeleted(); // feels hacky - don't think this is good practice, but does achieve my intention
db.Database.EnsureCreated();
try
{
InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}}", ex.Message);
}
}
});
}
private static void InitializeDbForTests(CompetitionStatsContext db)
{
db.Teams.Add(new CompetitionStats.Models.TeamDTO
{
Id = new Guid("3b477978-f280-11e9-8490-a8667f2f93c4"),
Name = "Arsenal"
});
db.SaveChanges();
}
}
}
}
Test class
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net.Http;
using System.Threading.Tasks;
namespace CompetitionStatsUnitTests.ControllerUnitTests
{
[TestClass]
public class TeamControllerTest
{
private HttpClient _testClient;
[TestInitialize]
public void Initialize()
{
var factory = new Utilities.CustomWebApplicationFactory<CompetitionStats.Startup>();
this._testClient = factory.CreateClient();
}
[TestMethod]
public async Task TeamController_GetTeam_Returns_Team()
{
var actualResponse = await this._testClient.GetStringAsync("api/teams/3b477978-f280-11e9-8490-a8667f2f93c4");
var expectedResponse = #"{""id"":""3b477978-f280-11e9-8490-a8667f2f93c4"",""name"":""Arsenal""}";
Assert.AreEqual(expectedResponse, actualResponse);
}
[TestMethod]
public async Task TeamController_PostTeam_Adds_Team()
{
var content = new StringContent(#"{""Name"": ""Liverpool FC""}", System.Text.Encoding.UTF8, "application/json");
var response = await this._testClient.PostAsync("api/teams/", content);
Assert.AreEqual(response.StatusCode, System.Net.HttpStatusCode.Created);
}
}
}
options.UseInMemoryDatabase("InMemoryDbForTesting");
This creates/uses a database with the name “MyDatabase”. If UseInMemoryDatabase is called again with the same name, then the same in-memory database will be used, allowing it to be shared by multiple context instances.
So you will get the error like{"An item with the same key has already been added. Key: 3b477978-f280-11e9-8490-a8667f2f93c4"} when you add data with the same Id repeatedly
You could add a judgment to the initialization method :
private static void InitializeDbForTests(CompetitionStatsContext db)
{
if (!db.Teams.Any())
{
db.Teams.Add(new Team
{
Id = new Guid("3b477978-f280-11e9-8490-a8667f2f93c4"),
Name = "Arsenal"
});
}
db.SaveChanges();
}
You could also refer to the suggestions provided by Grant says adios SE in this thread
I'm trying to create a Nancy/Unity-bootstrapper that can work with a OWIN-startup-class like the one described here:
Minimal sample: Self hosted Nancy using Owin, Unity bootstrapper including xUnit test
public class Startup
{
public void Configuration(IAppBuilder app)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IMessageHelper, MessageHelper>();
app.UseNancy(new NancyOptions
{
EnableClientCertificates = true,
Bootstrapper = new NancyOwinBoxBootstrapper(container)
});
}
}
public class NancyOwinBoxBootstrapper : UnityNancyBootstrapper
{
private IUnityContainer _container;
public NancyOwinBoxBootstrapper(IUnityContainer container)
{
_container = container;
}
protected override IUnityContainer GetApplicationContainer()
{
return _container;
}
}
The sample contained in the NancyOwinBox.zip works fine, but it is based on Nancy.Bootstrappers.Unity version 1.1. When i upgrade the NuGet package to the latest version (1.2) the sample fails with:
Resolution of the dependency failed, type = "Nancy.Bootstrapper.IApplicationStartup", name = "Nancy.ViewEngines.ViewEngineApplicationStartup".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, System.Collections.Generic.IEnumerable`1[Nancy.ViewEngines.IViewEngine], is an interface and cannot be constructed. Are you missing a type mapping?
At the time of the exception, the container was:
Resolving Nancy.ViewEngines.ViewEngineApplicationStartup,Nancy.ViewEngines.ViewEngineApplicationStartup (mapped from Nancy.Bootstrapper.IApplicationStartup, Nancy.ViewEngines.ViewEngineApplicationStartup)
Resolving parameter "viewEngines" of constructor Nancy.ViewEngines.ViewEngineApplicationStartup(System.Collections.Generic.IEnumerable1[[Nancy.ViewEngines.IViewEngine, Nancy, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null]] viewEngines, Nancy.ViewEngines.IViewCache viewCache, Nancy.ViewEngines.IViewLocator viewLocator)
Resolving System.Collections.Generic.IEnumerable1[Nancy.ViewEngines.IViewEngine],(none)
The current type, System.Collections.Generic.IEnumerable`1[Nancy.ViewEngines.IViewEngine], is an interface and cannot be constructed. Are you missing a type mapping?
Does anyone have an idea why this error occurs?
You need to register the EnumerableExtension in your container, like so:
container.AddNewExtension<EnumerableExtension>();
See UnityNancyBootstrapper.cs for Reference.
We have a need to be able to drop new projects/dlls into our main project and have the main project pick them up and be able to use them. It was decided to use AutoFac to handle this need. This way the main project would not need a direct reference to any of the other projects/dlls we want to use. Here is the global.asax:
var builder = new ContainerBuilder();
var directoryName = HttpContext.Current.Server.MapPath("~/bin/");
var cfg = new ModularityLoader(new ModularityConfig(Path.Combine(directoryName, #"Modules")), new Log4NetLogger(typeof(ModularityConfig)));
cfg.RegisterModulesFromCatalog(builder);
builder.RegisterType<AssembliesResolver>().AsImplementedInterfaces();
builder.RegisterType<AppsSecurityFeatureResolver>().AsImplementedInterfaces();
builder.RegisterType<FeaturesAutofacAuthoriztationFilter>()
.AsWebApiAuthorizationFilterFor<ApiController>().InstancePerApiRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
builder.RegisterControllers(typeof(MvcApplication).Assembly);
Container = builder.Build();
We have post-build events on all projects that copy their dlls into the bin directory of our main project. All of the dlls are loaded in here with var cfg = new ModularityLoader.
See image:
builder.Build() calls the following class in any of the dlls in our project that implements Autofac.Module such as this one:
public class AutofacModuleConfig : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterApiControllers(typeof(AutofacModuleConfig).Assembly);
}
}
Here is where the trouble with SignalR comes in. I would like to put RouteTable.Routes.MapHubs() into AutofacModuleConfig, but SignalR 2.0.0+ no longer supports this. Instead, it wants you to create a Startup class like this:
[assembly: OwinStartup("SignalRConfig", typeof(KL.Apps.TestHarness.SignalR.Startup))]
namespace KL.Apps.TestHarness.SignalR
{
public static class Startup
{
public static void Configuration(IAppBuilder app)
{
app.MapSignalR("/signalr", new HubConfiguration());
}
}
}
Note: To get this to work I added the following to the web.config:
<add key="owin:appStartup" value="SignalRConfig" />
SignalR also requires Hub classes like this:
[HubName("BatchHub")]
public class BatchHub : Hub
{
public void RemoveBatchRow(Guid batchId)
{
Clients.All.RemoveBatchRow(batchId);
}
}
Having the Startup class and Hub classes in my main project works perfectly. However, because of the unique nature of AutoFac, none of my Hubs from my external dlls are getting loaded up. I was thinking to try to find a way to force AutofacModuleConfig.Load to accept RouteTable.Routes.MapHubs(), but adding this line breaks the code.
I was thinking to go back to an earlier version of SignalR so I can use RouteTable.Routes.MapHubs() but I would really like to use the most up to date version.
Any ideas?
Thanks in advance.
EDIT 1:
I found this: https://code.google.com/p/autofac/wiki/SignalRIntegration
It seems to be the answer the the problem. However, RegisterHubs does not exist off of the builder object even after installing the Autofac.SignalR NuGet package...
var builder = new ContainerBuilder();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
EDIT 2:
So this is what my AutofacModuleConfig looks like now, but my hubs are still not showing up in javascript.
namespace OurName.IOC
{
public class AutofacModuleConfig : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
AutofacSignalRConfig.Load(builder);
builder.RegisterApiControllers(typeof(AutofacModuleConfig).Assembly);
}
}
}
namespace Autofac.Integration.SignalR
{
public static class AutofacSignalRConfig
{
public static void Load(ContainerBuilder builder)
{
builder.RegisterHubs(Assembly.GetExecutingAssembly());
}
}
}
Still not working though. In javascript I cannot see my hubs.
$(document).ready(function(){
var myHub = $.connection.BatchHub;
});
myHub is null.
EDIT 3:
Tried this last night. I removed AutofacSignalRConfig and this is how the main Global.asax looks now:
var builder = new ContainerBuilder();
var directoryName = HttpContext.Current.Server.MapPath("~/bin/");
var cfg = new ModularityLoader(new ModularityConfig(Path.Combine(directoryName, #"Modules")), new Log4NetLogger(typeof(ModularityConfig)));
cfg.RegisterModulesFromCatalog(builder);
builder.RegisterType<AssembliesResolver>().AsImplementedInterfaces();
builder.RegisterType<AppsSecurityFeatureResolver>().AsImplementedInterfaces();
builder.RegisterType<FeaturesAutofacAuthoriztationFilter>()
.AsWebApiAuthorizationFilterFor<ApiController>().InstancePerApiRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
builder.RegisterControllers(typeof(MvcApplication).Assembly);
foreach (var assembly in cfg.LoadedAssemblies)
{
var hubs = builder.RegisterHubs(assembly);
}
Container = builder.Build();
GlobalHost.DependencyResolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(Container);
Notice how I tried to take the output of cfg (which is a collection of all assemblies found in the bin folder) and then foreach through them in order to execute builder.RegisterHubs on each one. Still not picking up my hubs from any of those assemblies though.
Does there need to be a Startup class in each of my imported dlls too? If so, how would I go about calling it?
EDIT 4:
Tried this too with no success:
foreach (var assembly in cfg.LoadedAssemblies)
{
//var hubs = builder.RegisterHubs(assembly);
var a = assembly.GetExportedTypes().Where(x => x.BaseType == typeof (Hub));
foreach (var x in a)
{
builder.RegisterType(x);
}
}
See image for debug info:
EDIT 5:
Jim Bolla thank you for the input. Here is my first try at your suggestion.
public class AssemblyLocator : IAssemblyLocator
{
public IList<Assembly> GetAssemblies()
{
var directoryName = HttpContext.Current.Server.MapPath("~/bin/");
var cfg = new ModularityLoader(new ModularityConfig(Path.Combine(directoryName, #"Modules")), new Log4NetLogger(typeof(ModularityConfig)));
return cfg.LoadedAssemblies;
}
}
Also added this just above Container = builder.Build:
builder.RegisterType<AssemblyLocator>().As<IAssemblyLocator>().SingleInstance();
AssemblyLocator.GetAssemblies() is now getting called.
... Testing ...
EDIT 6:
Okay. So now when I throw a breakpoint into javascript, I can see $.connection.BatchHub which is great! However, when trying to call the hub method RemoveBatchRow on the server-side I get the following error:
"Using a Hub instance not created by the HubPipeline is unsupported."
Here is the stack trace:
at
Microsoft.AspNet.SignalR.Hubs.NullClientProxy.TryInvokeMember(InvokeMemberBinder
binder, Object[] args, Object& result) at CallSite.Target(Closure ,
CallSite , Object , Guid ) at
System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite
site, T0 arg0, T1 arg1) at
KL.Apps.WebIndex.Hubs.BatchHub.RemoveBatchRow(Guid batchId) in
d:\Source\Apps Framework
Modules\WebIndex\Main\Source\KL.Apps.WebIndex\Hubs\BatchHub.cs:line 20
at
KL.Apps.WebIndex.API.Batch.BatchLockController.TryLockBatchAsync(String
batchId) in d:\Source\Apps Framework
Modules\WebIndex\Main\Source\KL.Apps.WebIndex\API\Batch\BatchLockController.cs:line
30 at lambda_method(Closure , Object , Object[] ) at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.b__c(Object
instance, Object[] methodParameters) at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object
instance, Object[] arguments) at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1
func, CancellationToken cancellationToken)
I am calling the hub method from one of my remote dlls like this:
new BatchHub().RemoveBatchRow(gId);
You can see the definition for BatchHub above.
Any other ideas?
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-signalr-20-self-host
In my case, my hubs are in a project referenced from the project code that spins up the self-hosted application.
On the line connection.Start().Wait(); I get an exception. The following is the sequence of exceptions thrown at that line:
The specified registry key does not exist System.IO.IOException
'MessageHub' Hub could not be resolved InvalidOperationException
The remote server returned an error: (500) Internal Server Error WebException
The signature of the message hub class in the referenced project is public class MessageHub : Hub.
Update: To test the theory, I moved the hub class from the referenced project into my test project and updated the namespace. It worked. So I think the theory here is sound... default hub resolution does not find hubs in referenced project or in separate namespace.
How can I convince MapHubs to find the test hub in the referenced project?
I think that I have found an answer to this.
After doing some digging around the source code it seems that SignalR uses the following method to designate an IAssemblyLocator to locate Hubs.
internal static RouteBase MapHubs(this RouteCollection routes, string name, string path, HubConfiguration configuration, Action<IAppBuilder> build)
{
var locator = new Lazy<IAssemblyLocator>(() => new BuildManagerAssemblyLocator());
configuration.Resolver.Register(typeof(IAssemblyLocator), () => locator.Value);
InitializeProtectedData(configuration);
return routes.MapOwinPath(name, path, map =>
{
build(map);
map.MapHubs(String.Empty, configuration);
});
}
public class BuildManagerAssemblyLocator : DefaultAssemblyLocator
{
public override IList<Assembly> GetAssemblies()
{
return BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();
}
}
public class DefaultAssemblyLocator : IAssemblyLocator
{
public virtual IList<Assembly> GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
}
This got me to try to simply add my external assembly to current domain because although it was referenced it was not being loaded.
So before calling WebApp.Start I call the following line.
static void Main(string[] args)
{
string url = "http://localhost:8080";
// Add this line
AppDomain.CurrentDomain.Load(typeof(Core.Chat).Assembly.FullName);
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
Where Core.Chat is simply the Hub class I'm using.
And then the hubs defined in referenced assembly are loaded.
There might be a more straight forward way to go about this but I could not find anything in the documentation.
Hope this helps.
I want to write a unit test that verifies my route registration and ControllerFactory so that given a specific URL, a specific controller will be created. Something like this:
Assert.UrlMapsToController("~/Home/Index",typeof(HomeController));
I've modified code taken from the book "Pro ASP.NET MVC 3 Framework", and it seems it would be perfect except that the ControllerFactory.CreateController() call throws an InvalidOperationException and says This method cannot be called during the application's pre-start initialization stage.
So then I downloaded the MVC source code and debugged into it, looking for the source of the problem. It originates from the ControllerFactory looking for all referenced assemblies - so that it can locate potential controllers. Somewhere in the CreateController call-stack, the specific trouble-maker call is this:
internal sealed class BuildManagerWrapper : IBuildManager {
//...
ICollection IBuildManager.GetReferencedAssemblies() {
// This bails with InvalidOperationException with the message
// "This method cannot be called during the application's pre-start
// initialization stage."
return BuildManager.GetReferencedAssemblies();
}
//...
}
I found a SO commentary on this. I still wonder if there is something that can be manually initialized to make the above code happy. Anyone?
But in the absence of that...I can't help notice that the invocation comes from an implementation of IBuildManager. I explored the possibility of injecting my own IBuildManager, but I ran into the following problems:
IBuildManager is marked internal, so I need some other authorized derivation from it. It turns out that the assembly System.Web.Mvc.Test has a class called MockBuildManager, designed for test scenarios, which is perfect!!! This leads to the second problem.
The MVC distributable, near as I can tell, does not come with the System.Web.Mvc.Test assembly (DOH!).
Even if the MVC distributable did come with the System.Web.Mvc.Test assembly, having an instance of MockBuildManager is only half the solution. It is also necessary to feed that instance into the DefaultControllerFactory. Unfortunately the property setter to accomplish this is also marked internal (DOH!).
In short, unless I find another way to "initialize" the MVC framework, my options now are to either:
COMPLETELY duplicate the source code for DefaultControllerFactory and its dependencies, so that I can bypass the original GetReferencedAssemblies() issue. (ugh!)
COMPLETELY replace the MVC distributable with my own build of MVC, based on the MVC source code - with just a couple internal modifiers removed. (double ugh!)
Incidentally, I know that the MvcContrib "TestHelper" has the appearance of accomplishing my goal, but I think it is merely using reflection to find the controller - rather than using the actual IControllerFactory to retrieve a controller type / instance.
A big reason why I want this test capability is that I have made a custom controller factory, based on DefaultControllerFactory, whose behavior I want to verify.
I'm not quite sure what you're trying to accomplish here. If it's just testing your route setup; you're way better off just testing THAT instead of hacking your way into internals. 1st rule of TDD: only test the code you wrote (and in this case that's the routing setup, not the actual route resolving technique done by MVC).
There are tons of posts/blogs about testing a route setup (just google for 'mvc test route'). It all comes down to mocking a request in a httpcontext and calling GetRouteData.
If you really need some ninja skills to mock the buildmanager: there's a way around internal interfaces, which I use for (LinqPad) experimental tests. Most .net assemblies nowadays have the InternalsVisibleToAttribute set, most likely pointing to another signed test assembly. By scanning the target assembly for this attribute and creating an assembly on the fly that matches the name (and the public key token) you can easily access internals.
Mind you that I personally would not use this technique in production test code; but it's a nice way to isolate some complex ideas.
void Main()
{
var bm = BuildManagerMockBase.CreateMock<MyBuildManager>();
bm.FileExists("IsCool?").Dump();
}
public class MyBuildManager : BuildManagerMockBase
{
public override bool FileExists(string virtualPath) { return true; }
}
public abstract class BuildManagerMockBase
{
public static T CreateMock<T>()
where T : BuildManagerMockBase
{
// Locate the mvc assembly
Assembly mvcAssembly = Assembly.GetAssembly(typeof(Controller));
// Get the type of the buildmanager interface
var buildManagerInterface = mvcAssembly.GetType("System.Web.Mvc.IBuildManager",true);
// Locate the "internals visible to" attribute and create a public key token that matches the one specified.
var internalsVisisbleTo = mvcAssembly.GetCustomAttributes(typeof (InternalsVisibleToAttribute), true).FirstOrDefault() as InternalsVisibleToAttribute;
var publicKeyString = internalsVisisbleTo.AssemblyName.Split("=".ToCharArray())[1];
var publicKey = ToBytes(publicKeyString);
// Create a fake System.Web.Mvc.Test assembly with the public key token set
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "System.Web.Mvc.Test";
assemblyName.SetPublicKey(publicKey);
// Get the domain of our current thread to host the new fake assembly
var domain = Thread.GetDomain();
var assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = assemblyBuilder.DefineDynamicModule("System.Web.Mvc.Test", "System.Web.Mvc.Test.dll");
AppDomain currentDom = domain;
currentDom.TypeResolve += ResolveEvent;
// Create a new type that inherits from the provided generic and implements the IBuildManager interface
var typeBuilder = moduleBuilder.DefineType("Cheat", TypeAttributes.NotPublic | TypeAttributes.Class, typeof(T), new Type[] { buildManagerInterface });
Type cheatType = typeBuilder.CreateType();
// Magic!
var ret = Activator.CreateInstance(cheatType) as T;
return ret;
}
private static byte[] ToBytes(string str)
{
List<Byte> bytes = new List<Byte>();
while(str.Length > 0)
{
var bstr = str.Substring(0, 2);
bytes.Add(Convert.ToByte(bstr, 16));
str = str.Substring(2);
}
return bytes.ToArray();
}
private static ModuleBuilder moduleBuilder;
private static Assembly ResolveEvent(Object sender, ResolveEventArgs args)
{
return moduleBuilder.Assembly;
}
public virtual bool FileExists(string virtualPath) { throw new NotImplementedException(); }
public virtual Type GetCompiledType(string virtualPath) { throw new NotImplementedException(); }
public virtual ICollection GetReferencedAssemblies() { throw new NotImplementedException(); }
public virtual Stream ReadCachedFile(string fileName) { throw new NotImplementedException(); }
public virtual Stream CreateCachedFile(string fileName) { throw new NotImplementedException(); }
}