I want to get a link to image resource in a MVC view that is part of an Orchard module.
Googling a bit resulted in the following approach:
https://stackoverflow.com/a/9256515/3936440
and its using #Html.ResourceUrl() in views to get the resource URL.
I wonder where the ResourceUrl() comes from as its not documented in MSDN and i cannot use it in my projects either.
Did someone used that approach already and can shed some light on whats missing here?
Update:
I figured it out. The following code works in connection with Orchard modules.
First you need to add a resource manifest to the Orchard module like so
public class ResourceManifest : Orchard.UI.Resources.IResourceManifestProvider
{
public void BuildManifests(Orchard.UI.Resources.ResourceManifestBuilder aBuilder)
{
Orchard.UI.Resources.ResourceManifest lManifest = aBuilder.Add();
string lModulePath = "~/Modules/YourModuleName";
lManifest.DefineResource("ProfilePicture", "User1").SetUrl(lModulePath + "/Images/User1.png");
}
}
Then you extend the Html object:
// This class adds so called "extension methods" to class System.Web.Mvc.HtmlHelper
public static class HtmlHelperExtensions
{
// This method retrieves the URL of a resource defined in ResourceManifest.cs via the Orchard resource management system
public static string ResourceUrl(this System.Web.Mvc.HtmlHelper aHtmlHelper, string aResourceType, string aResourceName)
{
// note:
// resolving Orchard.UI.Resources.IResourceManager via work context of orchard because
// - calling System.Web.Mvc.DependencyResolver.Current.GetService() does not work as it always returns null at this point
// - constructor parameter injection is not allowed in static classes
// - setting the resource manager from another class that uses constructor parameter injection does not work as it causes a "circular component dependency "
Orchard.WorkContext lWorkContext = Orchard.Mvc.Html.HtmlHelperExtensions.GetWorkContext(aHtmlHelper);
Orchard.UI.Resources.IResourceManager lResourceManager = (Orchard.UI.Resources.IResourceManager)lWorkContext.Resolve<Orchard.UI.Resources.IResourceManager>();
if (lResourceManager != null)
{
Orchard.UI.Resources.RequireSettings lSettings = new Orchard.UI.Resources.RequireSettings { Type = aResourceType, Name = aResourceName, BasePath = aResourceType };
Orchard.UI.Resources.ResourceDefinition lResource = lResourceManager.FindResource(lSettings);
if (lResource != null)
{
Orchard.UI.Resources.ResourceRequiredContext lContext = new Orchard.UI.Resources.ResourceRequiredContext { Resource = lResource, Settings = lSettings };
string lAppBasePath = System.Web.HttpRuntime.AppDomainAppVirtualPath;
return lContext.GetResourceUrl(lSettings, lAppBasePath);
}
}
return null;
}
}
and then you can write:
<img src="#Html.ResourceUrl("ProfilePicture", "User1")" />
in an Orchard module view to get appropriate image link for User1.
I hope this helps.
ResourceUrl() is a custom HtmlHelper extension.
The code that you need to implement it is included in the answer you have linked.
You simply need to create a static class that contains the method code.
Asp.net article on how to create custom html helpers
PS: Make sure you import your namespace into the view with #using YourNamespace or add it to the System.Web.Mvc.HtmlHelper class.
Related
I've converted my project from .Net framework 4 to 4.5.
I have done this to make use of the Nuget package MvcMailer.
All is good except in the UserMailer class the following code exists:
public virtual MvcMailMessage Welcome()
{
//ViewBag.Data = someObject;
return Populate(x =>
{
x.Subject = "Welcome";
x.ViewName = "Welcome";
x.To.Add("some-email#example.com");
});
}
The Populate word throws an error:
The name 'Populate' does not exist in the current context
To what Namespace does the word Populate belong to?
Or is it an extension?
I can't find anything on the net.
It's a class method of MailerBase controller with this signature (from source code on GitHub):
public virtual MvcMailMessage Populate(Action<MvcMailMessage> action)
To use it you must derive your controller from MailerBase (it's the base class for Mailers. Your mailer should subclass MailerBase).
For example, supposing your controller is named Home, from:
public class Home : Controller {
To:
public class Home : MailerBase {
It's in Mvc.Mailer namespace (same of MvcMailerMessage class) anyway it's not an extension method so you don't even need to worry about it.
Put the cursor on the keyword and hit Alt+Shift+F10. It will show you it's source, and you'll be able to include the whole namespace, or use the keyword just once. It will only work if you have a correct .dll reference in your project.
I think you got the code from this GitHub repository.
https://github.com/smsohan/MvcMailer/wiki/MvcMailer-Step-by-Step-Guide
public virtual MvcMailMessage Welcome()
{
ViewBag.Name = "Sohan";
return Populate(x =>{
x.viewName = "Welcome";
x.To.Add("sohan39#example.com");
});
}
This code was provided by the Developer of the Package and he showed how to use it to edit the MvcMailer.
If so, the guy there used these namespaces in the top of his C# file.
using Mvc.Mailer;
So, I guess it would be a part of this Namespace. Include it to your project and you're done!
So, I'm trying to find the Umbraco node (as iPublishedContent), and pass it to the viewModel (as ะจ've hijacked a route). So i put this in my controller:
private AddCouponCodesViewModel viewModel;
public AddCouponCodesController(){
//Get iPublished content
IPublishedContent content = Umbraco.TypedContent(1225);
//Pass to viewModel
viewModel = new AddCouponCodesViewModel(content);
RouteData.DataTokens["umbraco"] = content;
}
public ActionResult Index()
{
//return view etc
}
But I'm getting
Exception Details: System.NullReferenceException:
Object reference not set to an instance of an object.
here:
Source Error(AddCouponCodesViewModel.cs):
Line 20:
Line 21: }
Line 22: public AddCouponCodesViewModel(IPublishedContent content)
Line 23: : base(content)
Line 24: {
AddCouponCodeRenderModel.cs:
public class AddCouponCodesViewModel : RenderModel
{
public string test { get; set; }
public List<string> tables { get; set; }
public List<string> errors { get; set; }
public AddCouponCodesViewModel(IPublishedContent content, CultureInfo culture) : base(content, culture)
{
}
public AddCouponCodesViewModel(IPublishedContent content)
: base(content)
{
}
And this is the Global.asax
public class Global : UmbracoApplication
{
protected override void OnApplicationStarted(object sender, EventArgs e)
{
base.OnApplicationStarted(sender, e);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//AreaRegistration.RegisterAllAreas();
//WebApiConfig.Register(GlobalConfiguration.Configuration);
//FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
base.OnApplicationStarting(sender, e);
RouteTable.Routes.MapRoute(
"AddCouponCodes", // Route name
"Admin/{controller}/{action}/{id}", // URL with parameters
new { controller = "AddCouponCodes", action = "Index", id = "" } // Parameter defaults
);
}
}
The content is published (I've checked and double checked), and the node ID is correct.
What I'm basically trying to do here, is to get the route example.com/Admin/{controller}/{action}/{parameter}
To be routed, but having problems connecting it with the umbracoNode (And class RenderModel requires a iPublishContent object as a parameter, but I'm in no luck when trying to pass it anything)
Could someone please help me here, been stuck way too many hours on this :-(
To clarify, if you are hijacking a route, it means that you are overriding the way Umbraco passes it's RenderModel to one of it's published pages. You can either do this globally by overriding the main RenderMvcController, or you can override on a DocumentType-specific basis. So for example, if I have a Homepage doc type, I could create:
public HomepageController : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
// Create your new renderModel here, inheriting
// from RenderModel
return CurrentTemplate(renderModel);
}
}
This would route all calls to the homepage through this one action. For this, you don't need to define any new routes in the route table. And you should override the render model in the action not in the constructor.
Your question is slightly confusing and it's not entirely clear what you are trying to achieve because:
You have defined a route, and
In your constructor you are calling Umbraco.TypedContent(1225) to retrieve a specific published node
So ... if the admin page you are trying to route has itself been published by Umbraco (and it doesn't sound like it has), the just create a new controller with the name of the page's document type and override the render model in the way described above.
However ... if your admin page hasn't been published by Umbraco and you just want the admin page to access node data, then you have a couple of options:
Create a surface controller, inheriting from SurfaceController. This will give you access to the Umbraco context et al; or
Create a standard controller (preferrably in an Area) and inject the ContentCache using something like Autofac
E.g.:
builder.RegisterControllers(typeof (AdminController).Assembly)
.WithParameter("contentCache", UmbracoContext.Current.ContentCache);
Create a standard controller (preferrably in an Area) and access the node using Umbraco's ContentService API, i.e. new Umbraco.Core.Services.ContentService().GetById(1225)
The difference between the last two approaches is that:
Injecting the ContentCache provides you readonly but very quick access to the published content.
Accessing the ContentService provides you read/write access to the nodes themselves but at the expense of speed as you are querying the database directly.
It depends on what your requirement is.
Either way, it is well worth taking time to read through the documentation for hijacking Umbraco routes, and at least trying to understand what is going on.
Well, I can tell you that your view isn't getting fed anything for the Razor markup because your Index method doesn't feed it anything. That's one problem. I can also tell you, that in your AddCouponCodesViewModel, you'll need an empty constructor, so that the razor syntax can just create an instance, and then populate it to match your submitted object to the view.
Modify your ViewController :
public ActionResult Index()
{
return View(viewModel);
}
Modify your AddCouponCodesViewModel to add an Empty constructor:
public AddCouponCodesViewModel()
{
}
Create a paramaterless constructor on your view model like this:
public AddCouponCodesViewModel():
this(new UmbracoHelper(UmbracoContext.Current).
TypedContent(UmbracoContext.Current.PageId))
{
}
This will get the contexts your other constructors are looking for.
After you've created a class with specific constructors, the compiler stops generating a parameterless one by default. Since you need a parameterless constructor, this is how to get one and still pass in the Umbraco contextual info your viewmodel needs
The default Global.asax.cs file from the "WCF REST Template 40(CS)" project template and every tutorial I've seen online include a variation of the following method:
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
RouteTable.Routes.Add(new ServiceRoute("Service1", new WebServiceHostFactory(), typeof(Service1)));
}
Managing the service routing in this way seems needlessly cumbersome when the WebApplication itself should be able to discover which services should be available and apply routes based on convention or metadata.
QUESTIONS
Is there a built-in way beyond the default to define the service routes (either configurable in the web.config, or compiled onto the service itself)?
Do others that use this template always follow the model provided or has someone else come up with a better approach?
Proposed Solution
Migrated my proposed solution to an answer
I guess I have to assume that silence is acceptance. Here is my solution (originally from my question):
Assuming there is nothing better built in or otherwise available (because I didn't find anything), my attempt at doing this involves defining an attribute:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class ServiceRouteAttribute : Attribute
{
public string RoutePrefix { get; set; }
public Type ServiceFactoryType { get; set; }
public ServiceHostFactoryBase ServiceFactory
{
get
{
if (ServiceFactoryType == null || !ServiceFactoryType.IsRelated(typeof(ServiceHostFactoryBase)))
return null;
return Activator.CreateInstance(ServiceFactoryType) as ServiceHostFactoryBase;
}
}
public ServiceRouteAttribute() : this(string.empty) { }
public ServiceRouteAttribute(string routePrefix) : this(routePrefix, typeof(WebServiceHostFactory)) { }
public ServiceRouteAttribute(string routePrefix, Type serviceFactoryType)
{
RoutePrefix = routePrefix;
ServiceFactoryType = serviceFactoryType;
}
}
which is used to decorate each service contract that should be exposed, and changing the default RegisterRoutes to:
private void RegisterRoutes()
{
// `TypeHelper.GetTypes().FilterTypes<T>` will find all of the types in the
// current AppDomain that:
// - Implement T if T is an interface
// - Are decorated with T if T is an attribute
// - Are children of T if T is anything else
foreach (var type in TypeHelper.GetTypes()
.FilterTypes<ServiceRouteAttribute>())
{
// routeAttrs should never be null or empty because only types decorated
// with `ServiceRouteAttribute` should ever get here.
// `GetAttribute<T>` is my extension method for `MemberInfo` which returns all
// decorations of `type` that are T or children of T
var routeAttrs = type.GetAttributes<ServiceRouteAttribute>();
foreach (var routeAttr in routeAttrs)
{
// Some dupe and error checking
var routePrefix = routeAttr.RoutePrefix;
if (string.IsNullOrEmpty(routePrefix))
routePrefix = type.Name;
RouteTable.Routes.Add(new ServiceRoute(routePrefix,
routeAttr.ServiceFactory,
type));
}
}
}
This seems to work and isn't too intrusive because it only happens at Application_Start, but I'm new to building RESTful web services with WCF4 so I don't know what sort of problems it could cause.
If anyone comes up with a more elegant way of solving this, I'd gladly consider any alternative.
Right now we have a dll file that contains all the database calls and i can't change it. However i need to call i from my Mvc 3 project. The process to call it is simple, i use the following:
ManageProvider.GetProxy<T>(ident);
T is an interface that i want to get the class back from (its like an IoC of its own) and ident is the user identification class. So by calling
var classReturned = ManageProvider.GetProxy<ICommunity>(new UserIden{ Email = "test#test.com" });
I would get a class back with all the community functions.
Now i want to implement Unity in my Mvc 3 project. The question is, can i somehow add these calls to the dll file through unity?
I want to resolve the call by using:
var classReturned = myContainer.Resolve<ICommunity>(new UserIden{ Email = "test#test.com" });
How can i register this in Unity (or is it even possible) ?
Update:
1) Is it better to call the methods with the email/user ident instead of defining a Dependency property? (ex below)
2) There is a bout 20 or so interfaces in the dll file right now. Should i add them all to the same reposatory? (ex below)
public class ProxyWrapper : IDllRepository
{
[Dependency]
public UserIdent UserIdent { get; set; }
public ICommunity GetCommunity()
{
return ManageProvider.GetProxy<ICommunity>(UserIdent);
}
public IDesktop GetDesktop()
{
return ManageProvider.GetProxy<IDesktop>(UserIdent);
}
}
public interface IDllRepository
{
ICommunity GetCommunity();
IDesktop GetDesktop();
}
Whats the best way and how would i call it from my code?
Does the [Dependency] attribute also fall into the Service Locator anti pattern?
Update 23.05.11
1) Yes, something like that. They contain all the logic that is provided to all the projects that includes the dll file.
Regarding the ManagerProvider. It accepts an interface and returns the class that is mapped to this interface. So for the community, the interface looks like this (removed a lot of calls to keep it short, there is also posts, comments, community create/update etc):
List<CommunityThread> GetThreads(int pStartRowIndex, int pMaximumRows, string pOrderBy, string pSearchExpression);
Guid? CreateThread(string pTitle, string pDescription, string pPostContent);
bool DeleteThread(Guid pThreadId);
List<CommunityThread> GetCommunityUserThreads(Guid pCommunityUserId);
2) What i can't update is how the ManageProvider.GetProxy works. The GetProxy is a class in the dll file that is hardcoded. Here is the part for the community. The class does the same for all the other interfaces as well, if typeof(interface) ... return class.
private static IManageProxy GetProxyForInterface<T>(UserIdent pIdent)
{
....
if (typeof(T).Equals(typeof(ICommunity)))
return new PCommunity();
....
}
3) Once registered using this new wrapper class, i can call it through the following code (MvcUnityContainer is a static class that only has a property called Container):
var c = MvcUnityContainer.Container.Resolve<IBackendRepository>(new PropertyOverride("UserIdent",
new UserIdent()));
Global.asax
IUnityContainer container = InitContainer();
MvcUnityContainer.Container = container;
DependencyResolver.SetResolver(new UnityMvcResolver(container));
The question is, do i need the static class MvcUnityContainer? Is it possible to configure the DependecyResolver to do that for me? Something like (problem is that it doesn't accept the override parameter):
var c = DependencyResolver.Current.GetService<IBackendRepository>(new PropertyOverride("UserIdent", new UserIdent()));
I think you need to hide the creation behind another abstraction, for instance:
public interface ICommunityRepository
{
ICommunity GetByEmailAddress(string address);
}
public class ManageProviderCommunityRepository
: ICommunityRepository
{
public ICommunity GetByEmailAddress(string address)
{
var id = new UserIden { Email = address };
return ManageProvider.GetProxy<ICommunity>(id);
}
}
This will hide both the ManageProvider and the UserIden behind this abstraction, and allows you to replace it later on with something more useful and makes testing easier.
Registration now is very easy:
RegisterType<ICommunityRepository, ManageProviderCommunityRepository>();
Instead of calling myContainer.Resolve (as you do in your example), inject the dependencies in your classes. This prevents you from using the Service Locator anti-pattern.
Perhaps you could do something like this, using the InjectionFactory:
myContainer.RegisterType<ICommunity>(
new InjectionFactory(c => ManageProvider.GetProxy<ICommunity>(new UserIden {Email = "test#test.com"})));
var classReturned = myContainer.Resolve<ICommunity>();
... Though you wouldn't be able to pass the UserIden as a parameter to the Resolve call, so I'm not sure if this is what you want.
To register all the public classes of the assembly you could perhaps iterate over Assembly.GetTypes() and register them in the same way?
I'm trying to figure out MEF's Constructor Injection attribute. I have no idea how I tell it to load the constructor's parameters.
This is the property I'm trying to load
[ImportMany(typeof(BUsers))]
public IEnumerable<BUsers> LoadBUsers { get; set; }
Here is the code I'm using to import the assemblies.
try
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog("DI"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
Here is the class I'm trying to load
[Serializable]
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
When you use the ImportingConstructor attribute, the parameters to the constructor become imports. By default, what you are importing (the contract name) is based on the type of the parameter or property that your are importing into. So in this case the contract type for both your imports is string, and there's no real difference between the first and second parameter.
It looks like you are trying to use imports to supply configuration values, which isn't necessarily what it was designed for. To get it to do what you want, you should override the contract name for each of the parameters, like this:
[ImportingConstructor]
public EditProfile([Import("Method")] string Method, [Import("Version")] string Version)
{ }
Then you need exports for Method and Version in your container. One way to do this is just to add them directly:
var container = new CompositionContainer(catalog);
container.ComposeExportedValue("Method", "MethodValue");
container.ComposeExportedValue("Version", "2.0");
container.ComposeParts(this);
(Note that ComposeExportedValue is actually an extension method defined on the static AttributedModelServices class.)
If you want to read these values from a configuration file of some sort, you could create your own export provider which reads the configuration and provides the values in it as exports to the container.
An alternative way to handle this would be to just import an interface that provides access to the configuration values by name, and get the values you need from the body of the constructor.
I like Daniel's solution; however, only one thing I see is the tight coupling of parameter names between the actor (who creates CompopositionContrainer()) and Export part with [ImportingConstructor] for customized CTOR. For example, "Method" has two be matched in both places. It makes hard to maintain the Export part if the actor and Export part are in difference projects.
If it is possible, I would add the second CTOR to the Export part class. For example:
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(EditProfileParameters ctorPars)
: this(ctorPars.Method, ctorPars.Version) {}
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
The class of EditProfileParameters should be straightforward: two properties of Method and Version:
[Export]
public class EditProfileParameters{
public string Method { get; set; }
public string Version { get; set; }
}
The key point is to add Export attribute to the class. Then MEF should be able to map this class to the parameter of EditProfile's CTOR.
Here is example to add the Export part to container:
var container = new CompositionContainer(catalog);
var instance1 = new EditProfileParameters();
// set property values from config or other resources
container.ComposeExportedValue(instance1);
container.ComposeParts(this);
Although late to the game, here's another approach that leverages a lesser-known feature of MEF: Property Exports
public class ObjectMother
{
[Export]
public static EditProfile DefaultEditProfile
{
get
{
var method = ConfigurationManager.AppSettings["method"];
var version = ConfigurationManager.AppSettings["version"];
return new EditProfile(method,version);
}
}
}
No usages are required for ObjectMother for this to work, and no attributes are required on EditProfile.