I am trying to register a ICustomVirtualPathProvider in one of my modules. This is what I am trying to use:
public class AzureVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider
{
public IStaticDataStorageProvider StaticDataStorageProvider { get; set; }
public VirtualPathProvider Instance
{
get
{
return this;
}
}
public AzureVirtualPathProvider(IStaticDataStorageProvider staticDataStorageProvider)
{
StaticDataStorageProvider = staticDataStorageProvider;
}
public override bool FileExists(string virtualPath)
{
if (!virtualPath.Contains("StaticData")) return base.FileExists(virtualPath);
return true;
}
public override VirtualFile GetFile(string virtualPath)
{
if (!virtualPath.Contains("StaticData") || !StaticDataStorageProvider.IsCloud()) return base.GetFile(virtualPath);
return new CustomVirtualFile(StaticDataStorageProvider, virtualPath);
}
}
so in Module.Load I am setting:
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AzureVirtualPathProvider>().PropertiesAutowired().As<ICustomVirtualPathProvider>();
}
but this has not been picked up when Orchard calls this line in OrchardStartup.cs (in Orchard.Framework)
if (HostingEnvironment.IsHosted) {
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
}
}
I haver tried calling HostingEnvironment.RegisterVirtualPathProvider directly thus:
HostingEnvironment.RegisterVirtualPathProvider(new AzureVirtualPathProvider());
and tried to inject the dependency using property injecction:
builder.Register(c => new AzureVirtualPathProvider { StaticDataStorageProvider = c.Resolve<IStaticDataStorageProvider>() });
however the value for StaticDataStorageProvider is always null when AzureVirtualPathProvider is run.
I have tried moving AzureVirtualPathProvider to OrchardFramework but then it does not resolve StaticDataStorageProvider.
How do I get Orchard to load my CustomVirtualPathProvider?
In the end I did this:
public class OrchardShellEvents : IOrchardShellEvents
{
readonly ICustomVirtualPathProvider _customVirtualPathProvider;
public OrchardShellEvents(ICustomVirtualPathProvider customVirtualPathProvider)
{
_customVirtualPathProvider = customVirtualPathProvider;
}
public void Activated()
{
HostingEnvironment.RegisterVirtualPathProvider(_customVirtualPathProvider.Instance);
}
public void Terminating()
{
}
}
I don't know if this is the best solution but it worked and might help someone else.
Related
I'm trying to register a mapping convention to handle mapping from classes with Pascal Case names to classes with underscore names with postfix and prefix, and back again. I've tried to follow examples, but cannot get my head around how it's supposed to work.
This is one of the many things I've tried, that looks like it should work (in my opinion :)), but it doesn't seem to do anything:
public class PascalCaseEntity
{
public string CallingSystem { get; set; }
}
public class UnderscoreWithPrefixAndPostfixEntity
{
public string p_calling_system_ { get; set; }
}
public class PartsMappings
{
public void Apply()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<FromUnderscoreMapping>();
cfg.AddProfile<ToUnderscoreMapping>();
cfg.CreateMap<PascalCaseEntity, UnderscoreWithPrefixAndPostfixEntity>()
.WithProfile("ToUnderscoreMapping");
cfg.CreateMap<UnderscoreWithPrefixAndPostfixEntity, PascalCaseEntity>()
.WithProfile("FromUnderscoreMapping");
});
}
}
public class FromUnderscoreMapping : Profile
{
protected override void Configure()
{
RecognizePrefixes("p_");
RecognizePostfixes("_");
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
}
public override string ProfileName
{
get { return "FromUnderscoreMapping"; }
}
}
public class ToUnderscoreMapping : Profile
{
protected override void Configure()
{
RecognizeDestinationPrefixes("p_");
RecognizeDestinationPostfixes("_");
SourceMemberNamingConvention = new PascalCaseNamingConvention();
DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
}
public override string ProfileName
{
get { return "ToUnderscoreMapping"; }
}
}
What am I missing here?
I finally found a working solution. I created two profiles, one for each "direction", and added the mappings to them.
I'm not too happy with it, since I'd rather have the mappings in the same file (grouping them on business area). But at least it works... :)
I also tried putting the registrations in the same Profile, and using the .WithProfile("ToUnderscoreWithPrefix") method, but I didn't get that to work.
Mapper.Initialize(cfg =>
{
cfg.AddProfile(new ToUnderscoreWithPrefixMappings());
cfg.AddProfile(new FromUnderscoreWithPrefixMappings());
});
public class ToUnderscoreWithPrefixMappings : Profile
{
protected override void Configure()
{
RecognizeDestinationPrefixes("P", "p");
SourceMemberNamingConvention = new PascalCaseNamingConvention();
DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
CreateMap<PascalCaseEntity, UnderscoreWithPrefixAndPostfixEntity>();
}
public override string ProfileName { get; } = "ToUnderscoreWithPrefix";
}
public class FromUnderscoreWithPrefixMappings : Profile
{
protected override void Configure()
{
RecognizePrefixes("P_", "p_");
RecognizePostfixes("_");
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<UnderscoreWithPrefixAndPostfixEntity, PascalCaseEntity>();
}
public override string ProfileName { get; } = "FromUnderscoreWithPrefix";
}
When attempting to load an instantiated export with GetExports() (using a LINQ query described below), the method returns null. I notice that when I call GetExports without the LINQ query, the return value is Count: 0. This would indicate to me that MEF is failing to find any exports that have been composed in the container. I can see the ExportDefinition, however, when looking at Container.Catalog.Parts.ExportDefinitions. Any ideas on where I am going wrong? Everything up until the query seems to be working fine.
I have the following contract and metadata view declared and implemented:
public interface IMap
{
void Init();
int ParseData();
}
public interface IMapMetadata
{
string MapName { get; }
string DocumentType { get; }
}
[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
public class Map
{
public Map()
{
}
}
I am using the following code to load a directory that contains DLLs that satisfy this contract with:
public void LoadByDirectory(string zPath)
{
try
{
_catalog.Catalogs.Add(new DirectoryCatalog(zPath));
}
catch (Exception e)
{
String zErrMess = e.Message;
}
}
Using a LINQ query to get an export:
public IMap GetMapInstance(string zMapName)
{
IMap ndeMap;
_container = new CompositionContainer(_catalog);
_container.ComposeParts(this);
try
{
ndeMap = _container.GetExports<IMap, IMapMetadata>()
.Where(p => p.Metadata.MapName.Equals(zMapName))
.Select(p => p.Value)
.FirstOrDefault();
}
catch (Exception ex)
{
throw new Exception("Failed to load map " + zMapName + ": " + ex.Message, ex);
}
return ndeMap;
}
Calling the above method like this:
IMap map = mapFactory.GetMapInstance("Map");
returns null.
UPDATED
In addition to the answer below, I was forgetting to declare the interface on the map class, this resolves the issue (note I removed the DocumentType property):
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
public MapExportAttribute()
: base(typeof(IMap))
{
}
public string MapName { get; set; }
}
[MapExport(MapName="Map")]
public class Map : IMap
{
public Map()
{
}
public void Init()
{
throw new NotImplementedException();
}
public int ParseData()
{
throw new NotImplementedException();
}
}
It looks like you're missing the DocumentType meta-data on your export:
[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
[ExportMetadata("DocumentType", "???")]
public class Map
{
}
The simplest way to ensure you specify the correct meta-data is a custom export attribute:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
public MapExportAttribute() : base(typeof(IMap))
{
}
public string MapName { get; set; }
public string DocumentType { get; set; }
}
[MapExport(MapName = "Map")]
public class Map
{
}
Seeing a strange problem, getting empty metata pages for xml,json and jvs.
Using the following command line app. How does one debug these issues?
namespace ConsoleApplication2
{
public struct NativeUser
{
public int login;
public string group;
public string name;
}
[DataContract]
public class User
{
private NativeUser _native;
public User() { }
public User(NativeUser native)
{
_native = native;
}
public static implicit operator NativeUser(User user)
{
return user._native;
}
public static implicit operator User(NativeUser native)
{
return new User(native);
}
// ReSharper disable InconsistentNaming
[DataMember]
public int login
{
get { return _native.login; }
set { _native.login = value; }
}
[DataMember]
public string group
{
get { return _native.group; }
set { _native.group = value; }
}
[DataMember]
public string name
{
get { return _native.name; }
set { _native.name = value; }
}
}
[Description("GET account, all or by list of groups or by list of logins")]
[Route("/accounts/{groups}", "GET")]
[Route("/accounts/{logins}", "GET")]
[Route("/accounts/", "GET")]
public class Accounts : IReturn<User[]>
{
public string[] groups { set; get; }
public int[] logins { set; get; }
public Accounts() { }
public Accounts(params int[] logins)
{
this.logins = logins;
}
public Accounts(params string[] groups)
{
this.groups = groups;
}
}
public class Host : AppHostHttpListenerBase
{
public Host() : base("Test",
typeof(Accounts).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
public class Servce : IService
{
public object Get(Accounts request)
{
return new List<User>(){new User(new NativeUser())};
}
}
class Program
{
static void Main(string[] args)
{
var host = new Host();
host.Init();
host.Start("http://+:12345/");
global::System.Console.ReadLine();
}
}
}
Nm, found the bug :
public class Accounts : IReturn<User[]>
needs to be
public class Accounts : IReturn<List<User>>
Another very note worthy thing: All DTO's and objects being passed back and fourth in the DTO's require an empty constructor in order for the metata data to be properly generated.
Not sure if this is by design or a bug
I'm trying to use the Three20 TTPhotoViewController with MonoTouch. I've derived FacebookPhoto from TTPhoto and FacebookPhotoSource from TTPhotoSource and am now trying to invoke the TTPhotoViewController but I get the following exception when pushing the view controller:
Objective-C exception thrown. Name: NSInvalidArgumentException Reason: * -[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument
I noticed that the monotouch bindings in this github project: https://github.com/mono/monotouch-bindings/tree/492f68c3c2007f0638452cc8a5a762556db224ba/Three20/binding were missing the photoAtIndex binding, so I added that and recompiled them, but I haven't been able to figure out why I am getting this exception.
Here is how I'm invoking the TTPhotoViewController:
List<Photo> photoList = FacebookGraphApi.Instance.GetAlbumPhotos(album.id);
List<FacebookPhoto> fbPhotoList = photoList.Select(x => new FacebookPhoto(x)).ToList();
var photos = new TTPhotoViewController();
photos.PhotoSource = new FacebookPhotoSource(fbPhotoList);
NavController.PushViewController(photos, true);
Here is the definition of the TTPhotoSource
class FacebookPhotoSource : TTPhotoSource
{
List<FacebookPhoto> _photoList;
public FacebookPhotoSource (List<FacebookPhoto> photoList)
{
_photoList = photoList;
int i = 0;
foreach (FacebookPhoto photo in photoList) {
photo.PhotoSource = this;
photo.Index = i++;
}
}
public override string Title {
get {
return "Facebook Photos";
}
set {
throw new NotImplementedException();
}
}
public override int NumberOfPhotos {
get {
return _photoList.Count;
}
}
public override int MaxPhotoIndex {
get {
return _photoList.Count -1;
}
}
public override TTPhoto PhotoAtIndex(int photoIndex)
{
return _photoList[photoIndex];
}
}
and here is the definition of the FacebookPhoto:
class FacebookPhoto : TTPhoto
{
Photo _photo;
public FacebookPhoto(Photo photo)
{
_photo = photo;
}
public override string Caption {
get {
if(_photo.name == null)
return "";
return _photo.name;
}
set {
throw new NotImplementedException();
}
}
public override TTPhotoSource PhotoSource { get; set; }
public override int Index { get; set; }
public override SizeF Size {
get {
return new SizeF(_photo.width, _photo.height);
}
set {
throw new NotImplementedException();
}
}
public override string URLForVersion (int version)
{
switch (version) {
case 4:
return _photo.picture;
default:
return _photo.source;
}
}
}
Here is my problem. I have a presenter class, lets call it 'Presenter' that takes an IDataSource as a constructor argument. There are different implementations of the IDataSource interface. I would like to be able to pass some argument to Ninject and based on that argument one of several IDataSource implementations should by used. I've provided some sample code below. I think that my solution is really ugly and that there must be a smarter, cleaner way to do this. How are you guys solving this type of problem?
Here is my sample code
public class Presenter
{
public Presenter(IDataSource dataSource)
{
DataSource = dataSource;
}
private IDataSource DataSource { get; set; }
public List<string> GetData()
{
return DataSource.GetAll();
}
}
public class InMemoryDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> {"a", "b"};
}
}
public class DbDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> { "1", "2" };
}
}
public interface IDataSource
{
List<string> GetAll();
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<Presenter>().To<Presenter>().Named("Db");
Bind<Presenter>().To<Presenter>().Named("InMemory");
Bind<IDataSource>().To<InMemoryDataSource> ().WhenParentNamed("InMemory");
Bind<IDataSource>().To<DbDataSource>().WhenParentNamed("Db");
}
}
[Test]
public void Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var p = kernel.Get<Presenter>(x => x.Name == "InMemory");
foreach(var s in p.GetData())
{
Console.Out.WriteLine(s);
}
}
}
This depends on what you want to do. I assume that you want to use a different db for testing than for production. In this case would create the module with the production configuration in mind and simply Rebind everything for testing:
public class Presenter
{
public Presenter(IDataSource dataSource)
{
DataSource = dataSource;
}
private IDataSource DataSource { get; set; }
public List<string> GetData()
{
return DataSource.GetAll();
}
}
public class InMemoryDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> {"a", "b"};
}
}
public class DbDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> { "1", "2" };
}
}
public interface IDataSource
{
List<string> GetAll();
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<Presenter>().To<Presenter>();
Bind<IDataSource>().To<DbDataSource>();
}
}
[Test]
public void Run()
{
using (var kernel = new StandardKernel(new Module()))
{
kernel.Rebind<IDataSource>().To<InMemoryDataSource>();
var p = kernel.Get<Presenter>();
foreach(var s in p.GetData())
{
Console.Out.WriteLine(s);
}
}
}