MVC4: making the area work for beginner - asp.net-mvc

I wanted to try and use area (just trying out). I added a area foo to my project: right click on the project then add area.
That folder contains sub folders where I can add controllers, view, models etc. It also has a cs file fooAreaRegistration.cs where routing for the area is done.
using System.Web.Mvc;
namespace AreasExample.Areas.foo
{
public class fooAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "foo";
}
}
public override void RegisterArea(AreaRegistrationContext context )
{
context.MapRoute(
"foo_default",
"foo/{controller}/{action}/{id}",
new {controller = "Foo", action = "Index", id = UrlParameter.Optional }
);
}
}
}
Global.asax already has a function for registering area in app start
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
}
}
I also then created a controller Foo which already has a default Index action, after that I added a view to the action. Based on the context.MapRoute in fooAreaRegistration.cs, if I run the program and go to this link http://localhost:54421/foo/Foo shouldn't it be working?
It shows some error when I go to my area foo and controller Foo.
Error says
Server Error in '/' Application.
[A]System.Web.WebPages.Razor.Configuration.HostSection cannot be cast to
[B]System.Web.WebPages.Razor.Configuration.HostSection. Type A originates from 'System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_1.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'. Type B originates from 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'.
Is there something I'm missing? Do I need to add something?
Edit: I'm not sure if I should delete this post as I found the answer which is mentioned as answer below. But then it may be helpful for those who are following the same book(ASP.NET MVC4 in action).
Suggestions?

That was probably because I downloaded the project of the book from internet which had older version of razor, and after I created an area in that project it couldn't cast it to the latest version of razor(i'm guessing) as the warning as in visual studio says:
Warning 1 D:\Tutorial\mvc4ia-2012-06-
13\src4\Chapter13\AreasExample\Areas\foo\Views\Foo\Index.cshtml: ASP.NET runtime error:
[A]System.Web.WebPages.Razor.Configuration.HostSection cannot be cast to
[B]System.Web.WebPages.Razor.Configuration.HostSection. Type A originates from
'System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location
'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_1.0.0.0__31bf385
6ad364e35\System.Web.WebPages.Razor.dll'.
Type B originates from 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf385
6ad364e35\System.Web.WebPages.Razor.dll'. D:\Tutorial\mvc4ia-2012-06-
13\src4\Chapter13\AreasExample\Areas\foo\Views\Foo\Index.cshtml AreasExample

Related

No context type was found in the assembly EF6

I'm working on a nopCommerce based project. In order to update custom entity I want to enable EF migrations. So I run following command:
Enable-Migrations -StartUpProjectName nop.web -ContextProjectName Nop.Plugin.Payments.Deposit -verbose
and get the error:
Using StartUp project 'Nop.Web'.
System.Data.Entity.Migrations.Infrastructure.MigrationsException: No context type was found in the assembly 'Nop.Plugin.Payments.Deposit'.
at System.Data.Entity.Utilities.TypeFinder.FindType(Type baseType, String typeName, Func`2 filter, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
at System.Data.Entity.Migrations.Design.ToolingFacade.GetContextTypeRunner.Run()
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
at System.Data.Entity.Migrations.Design.ToolingFacade.GetContextType(String contextTypeName)
at System.Data.Entity.Migrations.EnableMigrationsCommand.FindContextToEnable(String contextTypeName)
at System.Data.Entity.Migrations.EnableMigrationsCommand.<>c__DisplayClass2.<.ctor>b__0()
at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
No context type was found in the assembly 'Nop.Plugin.Payments.Deposit'.
The context class is defined as following:
public class DepositTransactionObjectContext : DbContext, IDbContext
{
public DepositTransactionObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { }
public DepositTransactionObjectContext() { }
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
throw new System.NotImplementedException();
}
public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
throw new System.NotImplementedException();
}
public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
{
throw new System.NotImplementedException();
}
public void Detach(object entity)
{
throw new System.NotImplementedException();
}
public bool ProxyCreationEnabled { get; set; }
public bool AutoDetectChangesEnabled { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DepositTransactionMap());
base.OnModelCreating(modelBuilder);
}
public string CreateDatabaseInstallationScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
public void Install()
{
//It's required to set initializer to null (for SQL Server Compact).
//otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database"
Database.SetInitializer<DepositTransactionObjectContext>(null);
Database.ExecuteSqlCommand(CreateDatabaseInstallationScript());
SaveChanges();
}
public void Uninstall()
{
this.DropPluginTable("DepositTransaction");
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
I've created empty project and enabled migrations there. Then I've copied and adjusted Configuration.cs so it looks like this:
namespace Nop.Plugin.Payments.Deposit.Migrations
{
using Data;
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<DepositTransactionObjectContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(DepositTransactionObjectContext context)
{
}
}
}
However when I run Add-Migration I get following error:
Using StartUp project 'Nop.Web'.
Using NuGet project 'Nop.Plugin.Payments.Deposit'.
System.Data.Entity.Migrations.Infrastructure.MigrationsException: No migrations configuration type was found in the assembly 'Nop.Plugin.Payments.Deposit'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).
at System.Data.Entity.Utilities.TypeFinder.FindType(Type baseType, String typeName, Func`2 filter, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
at System.Data.Entity.Migrations.Utilities.MigrationsConfigurationFinder.FindMigrationsConfiguration(Type contextType, String configurationTypeName, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.FindConfiguration()
at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Run()
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
at System.Data.Entity.Migrations.Design.ToolingFacade.Scaffold(String migrationName, String language, String rootNamespace, Boolean ignoreChanges)
at System.Data.Entity.Migrations.AddMigrationCommand.Execute(String name, Boolean force, Boolean ignoreChanges)
at System.Data.Entity.Migrations.AddMigrationCommand.<>c__DisplayClass2.<.ctor>b__0()
at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
No migrations configuration type was found in the assembly 'Nop.Plugin.Payments.Deposit'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).
I read a lot of same questions and everywhere the reason was wrong project specified in the Enable-Migrations cmdlet (not one having context defined). But it's not my case as you can see.
What else could be a reason?
Soooo I've managed to solve the problem.
For investigation I've downloaded sources of Entity Framework and checked all methods mentioned in exception stack. In the method System.Data.Entity.Utilities.TypeFinder.FindType I've found such instruction:
var types = _assembly.GetAccessibleTypes()
.Where(t => baseType.IsAssignableFrom(t));
Which lead me to GetAccessibleTypes() method where I found this:
return assembly.DefinedTypes.Select(t => t.AsType());
Having DefinedTypes is a property of System.Reflection.Assembly class I've tried to get this property manually by loading my assembly in Powershell and getting this property:
$a = [System.Reflection.Assembly]::LoadFrom("P:\nopCommerce\Presentation\Nop.Web\Plugins\Payments.Deposit\Nop.Plugin.Payments.Deposit.dll")
$a.DefinedTypes
The result was emptyness as there were no types at all.
So I tried to get types in a different way:
$a.GetTypes()
The result was an error:
Exception calling "GetTypes" with "0" argument(s): "Unable to load one
or more of the requested types. Retrieve the LoaderExceptions property
for more information."
When I checked LoaderException property I've found following:
$Error[0].Exception.InnerException.LoaderExceptions
Could not load file or assembly 'Autofac, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=17863af14b0044da' or one of its
dependencies. The system cannot find the file specified. Could not
load file or assembly 'Nop.Services, Version=3.8.0.0, Culture=neutral,
PublicKeyToken=null' or one of its dependencies. The system cannot
find the file specified. Could not load file or assembly
'System.Web.Mvc, Version=5.2.3.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The
system cannot find the file specified. Could not load file or assembly
'System.Web.Mvc, Version=5.2.3.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The
system cannot find the file specified. Could not load file or assembly
'System.Web.Mvc, Version=5.2.3.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The
system cannot find the file specified. Could not load file or assembly
'System.Web.Mvc, Version=5.2.3.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The
system cannot find the file specified.
It looked .NET tried to load all assemblies, my assembly depended on and of course could not as they are not in same folder because they are shared, located elsewhere and loaded by start up project, not by mine.
So I've copied all required assemblies to same folder as mine and tried to Enable-Migrations again. This time it was performed without any errors!
I ask myself why wouldn't Enable-Migrations load all those assemblies itself as it takes start up project into consideration. But it's rather minor issue.
You also need to add -ProjectName parameter to specify where you want the migrations to go. So for example:
Enable-Migrations -ProjectName Nop.Plugin.Payments.Deposit -StartUpProjectName nop.web -ContextProjectName Nop.Plugin.Payments.Deposit -verbose
It should default to the project you have selected in the console window. https://coding.abel.nu/2012/03/ef-migrations-command-reference/

StructureMap, Web Api 2, and IUserStore error

I have just started experimenting with Web Api 2 and StructureMap, having installed StructureMap.MVC4 Nuget package. Everything seems to work fine until I tried to register a user. I got this error when this implementation of IHttpControllerActivator tried to instantiate a controller:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) { }
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
The error I got was:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily Microsoft.AspNet.Identity.IUserStore`1[[Microsoft.AspNet.Identity.EntityFramework.IdentityUser, Microsoft.AspNet.Identity.EntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], Microsoft.AspNet.Identity.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
I understand what the error is, but not entirely sure how to solve it. Is it correct to assume the default scanner in StructureMap could not find a default implementation of IUserStore? Here's the initialisation code I used:
ObjectFactory.Initialize(x => x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
}));
Any ideas please? Thanks.
EDIT:
I think I may have solved the initial issue using this:
x.For<Microsoft.AspNet.Identity.IUserStore<IdentityUser>>()
.Use<UserStore<IdentityUser>>();
But now there's another default instance StructureMap couldn't work out - the dbcontext. Here's the next error message I'm getting:
ExceptionMessage=StructureMap Exception Code: 202
No Default Instance defined for PluginFamily System.Data.Entity.DbContext, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Now I'm really lost...
The WithDefaultConventions() call won't pick up your DbContext and AspNet Identity implementations. You'll want to look at some of the other methods like SingleImplementationsOfInterface() and ConnectImplementationsToTypesClosing.
By default when I setup my StructureMap container, I will do the following configuration in order to ensure that StructureMap will always resolve the interfaces and base classes of my preferred class to my actual preferred class:
ioc.For<MyDbContext>().HybridHttpOrThreadLocalScoped().Use<MyDbContext>();
ioc.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<MyDbContext>();
For the new AspNet Identity classes, just subclass the generic classes they give you out of the box:
public class MyUserManager : UserManager<MyUser> { }
public class MyUserStore : UserStore<MyUser> { }
And then again, make sure StructureMap knows about these:
ioc.For<IUserStore<MyUser>>().Use<MyUserStore>();
ioc.For<UserStore<MyUser>>().Use<MyUserStore>();
ioc.For<UserManager<MyUser>>().Use<MyUserManager>();
Generally, you don't have to explicitly register every class with StructureMap, but with my DbContext and Identity classes, I prefer to have those explicity registered for maintenance purposes.
ericb: I can see the purpose of what you've posted but I can't quite get it to work. The MyUserManager class declaration "public class MyUserManager : UserManager { }" is complaining that the UserManager interface does not contain a constructor that takes 0 arguments?
Any help would be greatly appreciated!
Ps. This is by no means an answer but I'm not qualified enough to simply comment on your answer unfortunately!
Update: Found a solution here: Dependency Injection Structuremap ASP.NET Identity MVC 5
For clarity we replaced any of the above with the following in the IoC file:
x.For<Microsoft.AspNet.Identity.IUserStore<ApplicationUser>>()
.Use<Microsoft.AspNet.Identity.EntityFramework.UserStore<ApplicationUser>>();
x.For<System.Data.Entity.DbContext>().Use(() => new ApplicationDbContext());
I'm sure we're missing out on some extra benefits that ericb gets with his solution but we're not utilising anything that would take advantage of them.
There is a quick and easy workaround to this problem as well, and in many cases may be sufficient. Go to AccountController.cs and above the default constructor (the one with no params or code in it) add [DefaultConstructor] and resolve using structuremap.
[DefaultConstructor]
public AccountController()
{
}
Though the proper IoC solution is this...
For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>();
For<DbContext>().Use<ApplicationDbContext>(new ApplicationDbContext());
For<IAuthenticationManager>().Use(() => HttpContext.Current.GetOwinContext().Authentication);
Or you can try constructor injection method:
x.For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>()
.SelectConstructor(() => new UserStore<ApplicationUser>(new MyContext()));

Structuremap Using Profiles (version 2.6)

ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.Assembly("CloudAssembly"); // defines profile "Cloud"
scan.LookForRegistries();
});
x.Profile("Local", cfg =>
{
cfg.For<ICloudStorage>().
Use(() =>
new LocalStorage(HttpContext.Current.Server.MapPath("~")));
});
});
Then I try to set it to the "Local" profile so that ICloudStorage resolves to LocalStorage.
ObjectFactory.Container.SetDefaultsToProfile("Local");
ObjectFactory.Profile = "Local";
Then I get this exception when activating an object that depends on ICloudStorage:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily ICloudStorage, AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Am I doing something wrong with the profiles? I looked at the output of whatDoIHave and everything looks like it is configured properly.
Here's the relevant section of "whatDoIHave":
ICloudStorage (MyLibrary.ICloudStorage) Default Instance for Profile Local Instance is created by Func<object> function: System.Func`2[StructureMap.IContext,MyLibrary.ICloudStorage]
Scoped as: Transient
Default Instance for Profile Local Instance is created by Func<object> function: System.Func`2[StructureMap.IContext,MyLibrary.ICloudStorage]
Default Instance for Profile Cloud Configured Instance of CloudProviders.CloudStorage, CloudProviders, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
I simply removed ObjectFactory.Container.SetDefaultsToProfile("Local") which I was calling directly after the Initialize method to use a config file instead...
<StructureMap DefaultProfile="Local">
</StructureMap>
And it is now working properly...

StructureMap registry isn't working for generic interfaces

public class RepositoryRegistry : Registry
{
public RepositoryRegistry()
{
Scan(x =>
{
x.Assembly("MyApp.Data");
x.TheCallingAssembly();
x.WithDefaultConventions();
x.AddAllTypesOf(typeof(ILookupRepo<>));
});
var tmp = ObjectFactory.GetInstance<ILookupRepo<User>>();
Debug.WriteLine(ObjectFactory.WhatDoIHave());
}
}
Getting this error:
{"StructureMap Exception Code: 202 No Default Instance defined for PluginFamily MyApp.Data.Repositories.Lookup.ILookupRepo`1[[MyApp.Data.Context.User, MyApp.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], TolMobile.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
StructureMap can't seem to resolve IAnything<T>. Is there any reason why this would stop working? I've used this in another project in the exact same way and it resolves ok. The only difference is that the version of StructureMap I am now using is newer...
This is not a StructureMap issue - no type inherits from ILookupRepo<>.
This is an open generic type, not a base type! If you want to find types closing on ILookupRepo<>, then you need to scan like this:
Scan(x =>
{
x.Assembly("MyApp.Data");
x.TheCallingAssembly();
x.WithDefaultConventions();
x.ConnectImplementationsToTypesClosing(typeof(ILookupRepo<>));
});
There's a blog post on the topic.

Castle Windsor cannot find System.Messaging

I realise this question is very similar to this one, but unfortunately it doesn't quite solve my problem.
I have a console app with the following in my castle config section:
<component id="LegacyMessageFormatter"
service="System.Messaging.IMessageFormatter, System.Messaging"
type="MsmqLogProcessor.Core.Services.LegacyMessageFormatter, MsmqLogProcessor.Core"/>
I initially got an exception "The type name System.Messaging.IMessageFormatter, System.Messaging could not be located." when newing up my WindsorContainer like so:
var container = new WindsorContainer(new XmlInterpreter());
I've set Copy Local to true on System.Messaging. That works when I hit F5.
However, when I publish my console app and run it on a production box I get the same issue. I cannot see the System.Messaging dll in the ApplicationFiles folder after install, presumably because it thinks it's in the GAC.
I've tried copying the dll into this folder, but still no joy.
Any ideas what I'm missing? Is it even a good idea to specify a System assembly as a service?
Try using the fully qualified type name:
<component id='LegacyMessageFormatter'
service='System.Messaging.IMessageFormatter, System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
type='...'/>
Windsor does not lookup types in System assemblies, as you can see here
https://github.com/castleproject/Castle.Windsor/blob/master/src/Castle.Windsor/MicroKernel/SubSystems/Conversion/TypeNameConverter.cs#L170
it explicitly skips types defined in assemblies with a name starting with System.
Don't know why but you can workaround this by using your own ConversionManager
public class MyConversionManager : DefaultConversionManager
{
protected override void InitDefaultConverters()
{
Add(new SystemMessaging_TypeConverter(new TypeNameParser()));
base.InitDefaultConverters();
}
}
public class SystemMessaging_TypeConverter : TypeNameConverter
{
public SystemMessaging_TypeConverter(ITypeNameParser parser) : base(parser)
{
}
protected override bool ShouldSkipAssembly(System.Reflection.Assembly assembly)
{
return !assembly.FullName.StartsWith("System.Messaging") && base.ShouldSkipAssembly(assembly);
}
}
and install it in your container as follows:
var container = new WindsorContainer();
container.Kernel.AddSubSystem(SubSystemConstants.ConversionManagerKey, new MyConversionManager());
container.Install(Configuration.FromAppConfig());
And then register the service without the assembly name:
<component id="LegacyMessageFormatter"
service="System.Messaging.IMessageFormatter"
type="MsmqLogProcessor.Core.Services.LegacyMessageFormatter, MsmqLogProcessor.Core">

Resources