2014-02-13 153 views
0

我正在构建一个可扩展框架,以基于基于MVC4 WebAPI系统的基于客户端的业务逻辑和数据结构与我们的通用基础架构分离。无法实例化接口的实现

为此,我创建了一个接口IApiServiceEntryPoint像这样:

public interface IApiServiceEntryPoint : IDisposable 
{ 
    /// <summary> 
    /// Gets the name of the API Plugin 
    /// </summary> 
    string Name { get; } 

    /// <summary> 
    /// Registers the assembly in the application, 
    /// sets up the routes, and enables invocation of API requests 
    /// </summary> 
    void Register(RouteCollection routes); 

    /// <summary> 
    /// Gets the routing namespace of the plugin 
    /// </summary> 
    string UrlNameSpace { get; } 
} 

然后,在同一个组件中,我创建了一个PluginHelper类,如下所示:

public static class PluginHelper 
{ 
    private static readonly List<IApiServiceEntryPoint> _plugins = new List<IApiServiceEntryPoint>(); 
    public static List<IApiServiceEntryPoint> Plugins { get { return _plugins; } } 

    private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(typeof(PluginHelper)); 
    /// <summary> 
    /// Registers all IApiServiceEntryPoint plugin classes. 
    /// </summary> 
    /// <param name="pluginPath">The directory where the plugin assemblies are stored.</param> 
    public static void Register(string pluginPath, RouteCollection routes) 
    { 
     Logger.InfoFormat("Registering plugins found at \"{0}\"...", pluginPath); 
     foreach (var plugin in _plugins) 
     { 
      Logger.DebugFormat("Disposing plugin {0}...", plugin.Name); 
      plugin.Dispose(); 
     } 

     Logger.DebugFormat("Clearing the plugin cache..."); 
     _plugins.Clear(); 

     var libraryFiles = System.IO.Directory.GetFiles(pluginPath, "*.*") 
      .Where(fn => fn.ToLowerInvariant().EndsWith(".dll") 
       || fn.ToLowerInvariant().EndsWith(".exe")) 
      .ToList(); 
     Logger.DebugFormat("Found {0} assemblies in the plugin directory...", libraryFiles.Count); 

     var assemblies = libraryFiles.Select(lf => Assembly.LoadFrom(lf)) 
      .ToList(); 
     Logger.DebugFormat("Loaded {0} assemblies into memory: {1}", assemblies.Count, string.Join(", ", assemblies.Select(a=>a.FullName).ToArray())); 

     var pluginTypes = assemblies.Where(assy => assy != null) 
      .SelectMany(assy => assy.GetTypes()) 
      .Where(t => !t.IsInterface && !t.IsAbstract && t.Namespace != null) 
      .ToList(); 
     Logger.DebugFormat("Located a total of {0} classes.", pluginTypes.Count); 

     pluginTypes = pluginTypes.Where(t => t.IsTypeOf<IApiServiceEntryPoint>()) 
      .ToList(); 
     Logger.DebugFormat("Located a total of {0} plugin entry points.", pluginTypes.Count); 

     foreach (var type in pluginTypes) 
     { 
      Logger.DebugFormat("Registering plugin type '{0}'...", type.Name); 
      var plugin = (IApiServiceEntryPoint)Activator.CreateInstance(type); 
      Logger.InfoFormat("Registering plugin \"{0}\"...", plugin.Name); 
      plugin.Register(routes); 
      Logger.InfoFormat("Plugin \"{0}\" Registered.", plugin.Name); 
      _plugins.Add(plugin); 
     } 

     Logger.InfoFormat("All {0} plugin(s) have been registered.", Plugins.Count); 
    } 

    public static bool IsTypeOf<T>(this Type type) 
    { 
     return type.GetInterfaces().Any(t =>t.Name == typeof(T).Name); 
    } 
} 

注扩展方法IsTypeOf() ...我原本试图使用IsAssignableFrom()的形式实现这一点,但它似乎从来没有工作......我认为这可能与我的问题有关。

接下来,我在同一个部件中创建一个抽象类:

public abstract class ApiPlugin : IApiServiceEntryPoint, IAccessControl 
{ 
    private static readonly ILog Logger = log4net.LogManager.GetLogger(typeof(ApiPlugin)); 
    public abstract string Name { get; } 

    public virtual void Register(RouteCollection routes) 
    { 
     var rt = string.Format("{0}/{{controller}}/{{id}}", UrlNameSpace); 
     var nameSpace = this.GetType().Namespace; 
     Logger.DebugFormat("Route Template: {0} in namespace {1}...", rt, nameSpace); 
     var r = routes.MapHttpRoute(
      name: Name, 
      routeTemplate: rt, 
      defaults: new { id = RouteParameter.Optional, controller = "Default" } 
      ); 
     r.DataTokens["Namespaces"] = new[] { nameSpace }; 

     Logger.InfoFormat("Plugin '{0}' registered namespace '{1}'.", Name, nameSpace); 
    } 

    public abstract string UrlNameSpace { get; } 

    public bool IsAuthorized<T>(Func<T> method) 
    { 
     var methodName = method.Method.Name; 
     var userName = User.Identity.Name; 
     return 
      ValidateAccess(userName, methodName) && 
      ValidateLicense(userName, methodName); 
    } 

    protected virtual bool ValidateAccess(string userName, string methodName) 
    { 
     // the default behavior to allow access to the method. 
     return true; 
    } 

    protected virtual bool ValidateLicense(string userName, string methodName) 
    { 
     // the default behavior is to assume the user is licensed. 
     return true; 
    } 

    public abstract IPrincipal User { get; } 

    public abstract void Dispose(); 

    public virtual bool ClientAuthorized(object clientId) 
    { 
     return true; 
    } 
} 

到目前为止,一切都顺顺当当的工作。现在在自己的程序集中编写我的第一个插件。我把它很简单:

public class DefaultPlugin : ApiPlugin 
{ 
    private static readonly ILog Logger = log4net.LogManager.GetLogger(typeof(DefaultPlugin)); 

    [HttpGet] 
    public DateTime GetSystemTimeStamp() 
    { 
     if (IsAuthorized(GetSystemTimeStamp)) 
     { 
      return DateTime.UtcNow; 
     } 
     throw new AuthorizationException(); 
    } 

    public override string Name 
    { 
     get { return "Default API Controller"; } 
    } 


    public override string UrlNameSpace 
    { 
     get { return "Default"; } 
    } 

    public override System.Security.Principal.IPrincipal User 
    { 
     get { return new GenericPrincipal(new GenericIdentity("Unauthenticated User"), new[] { "None" }); } 
    } 

    public override void Dispose() 
    { 
     //TODO: Unregister the plugin. 
    } 
} 

我建这个,我在我的插件注册的引用调用这个插件的二进制文件目录在我的MVC项目。

PluginHelper.Register()方法被调用时,我发现插件类,但在下面一行:

var plugin = (IApiServiceEntryPoint)Activator.CreateInstance(type); 

我结束了以下InvalidCastException的被抛出:

Unable to cast object of type 'myPluginNameSpace.DefaultPlugin' to type 'myInterfaceNamespace.IApiServiceEntryPoint' 

这里的东西:它绝对是该接口的实现。

现在我已经做过这种插件的事情,所以我知道它可以工作,但对于我的生活,我无法弄清楚我做错了什么。我期望它与特定的构建/版本或者强大的命名有关?请指教。

+0

我还没有重新创建您的问题,但请看看这个链接是否可以帮助您: http://stackoverflow.com/questions/1596796/net-unable-to-cast-object-to-interface-it-implements – HOKBONG

+0

@HOKBONG这个链接肯定有帮助。我也在做同样的事情。 –

+0

@HOKBONG如果你想写一个小小的摘要,我会将它标记为答案。 –

回答

0

Per @HOKBONG,答案here与我所做的完全相同,所提议的解决方案像一个魅力一样工作。

0

尝试

public static bool IsTypeOf<T>(this Type type) 
{ 
    return typeof(T).IsAssignableFrom(type); 
} 

public static bool IsTypeOf<T>(this Type type) 
{ 
    return type.GetInterfaces().Any(t => t.FullName == typeof(T).FullName); 
} 

此外,试图抛出一个Debug.Assert的(typeof运算(T).IsInterface)只是要确定。

+0

这几乎是我开始的,它没有任何明显的影响。 –

+0

你有PostSharp或任何重写IL?我意识到这是多余的,但是如果明确地添加接口会发生什么。ex public class DefaultPlugin:ApiPlugin,IApiServiceEntryPoint?在您的项目中,您没有任何机会在事故中定义了2个ApiPlugin或IApiServiceEntryPoint(错误使用语句)。否则,你的PluginHelper代码看起来很好。它显示“共有1个插件入口点。”?尝试使用反射来执行构造函数而不是Activator.CreateNewInstance()和/或记录插件类型的接口。如果一切都失败,请考虑尝试MEF – scottt732