MEF based plugin system can't instance my plugins - c#

I've implemented a very small plugin system based on C# with MEF. The problem is, none of my plugins are instanced. In the Aggregate-Catalog I can see my plugin listed. But, after I'll compose these parts, there isn't my plugin in the plugin list, what I'm doing wrong?
Here's a snippet of my code:
Plugin-Loader:
[ImportMany(typeof(IFetchService))]
private IFetchService[] _pluginList;
private AggregateCatalog _pluginCatalog;
private const string pluginPathKey = "PluginPath";
...
public PluginManager(ApplicationContext context)
{
var dirCatalog = new DirectoryCatalog(ConfigurationManager.AppSettings[pluginPathKey]);
//Here's my plugin listed...
_pluginCatalog = new AggregateCatalog(dirCatalog);
var compositionContainer = new CompositionContainer(_pluginCatalog);
compositionContainer.ComposeParts(this);
}
...
And here, the plugin itself:
[Export(typeof(IFetchService))]
public class MySamplePlugin : IFetchService
{
public MySamplePlugin()
{
Console.WriteLine("Plugin entered");
}
...
}

Tested working sample.
Compile class library with code inside PluginNameSpace namespace and place it to the 'Test' folder which will be inside console app exe folder.
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Reflection;
using ConsoleApplication;
namespace ConsoleApplication
{
public interface IFetchService
{
void Write();
}
class PluginManager
{
[ImportMany(typeof(IFetchService))]
public IFetchService[] PluginList;
public PluginManager()
{
var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Test");
var pluginCatalog = new AggregateCatalog(dirCatalog);
var compositionContainer = new CompositionContainer(pluginCatalog);
compositionContainer.ComposeParts(this);
}
}
class Program
{
static void Main(string[] args)
{
var pluginManager = new PluginManager();
foreach (var fetchService in pluginManager.PluginList)
{
fetchService.Write();
}
Console.ReadKey();
}
}
}
// Separate class library
namespace PluginNameSpace
{
[Export(typeof(IFetchService))]
public class MySamplePlugin : IFetchService
{
public void Write()
{
Console.WriteLine("Plugin entered");
}
}
}

Related

"AfterRead" event in the v2.0.0 version

My goal is to change the value of a field after it is read. And I think it is possible running the "AfterRead" event.
My current version of the library is 2.0.0.
I have tested the next getting an error:
public class sealed MyClass: INotifyRead
{
// ...
public void AfterRead(EngineBase engine, AfterReadEventArgs e)
{
}
}
The error message is:
Cannot find the namespace "AfterReadEventArgs". Missing the "using" directive or assembly reference.
I have read the next code in the docs:
FileHelperEngine engine = new FileHelperEngine(typeof(Orders));
engine.BeforeReadRecord += new BeforeReadRecordHandler(BeforeEvent);
Orders[] res = engine.ReadFile("report.txt") as Orders[];
I don't know if it is needed to declare the delegate in the source code or it is enough the declaration of the event in the Mapping Class.
Thanks a lot!
The signature of the AfterRead event is different in 2.0.0.0. Here's a working example.
using System;
using System.Diagnostics;
using FileHelpers;
namespace ConsoleApp
{
[DelimitedRecord(";")]
public sealed class MyClass : INotifyRead
{
public string Field1;
public string Field2;
public void AfterRead(EngineBase engine, string line)
{
Field1 = "ModifiedValue1";
}
}
internal class Program
{
static void Main(string[] args)
{
FileHelperEngine engine = new FileHelperEngine(typeof(MyClass));
var records = engine.ReadString("Value1;Value2");
var firstRecord = records[0] as MyClass;
Debug.Assert(firstRecord.Field1 == "ModifiedValue1");
Console.Write("All OK. Field1 had the modified value.");
Console.ReadKey();
}
}
}

How do I start an Activity from a non-Activity class?

How can I start an Activity from a non-Activity in Xamarin.Forms?
In the main startup project, MyApp.Droid, I have MyActivity.cs.
In the PCL project, MyApp.Plugin.Android, I have a non-activity class, MyClass.cs.
In MyClass.cs I need to run some code in MyActivity.cs.
How can I do that?
Thanks.
MyActivity.cs:
public void MyMethod()
{
// [Working Code...]
}
MyClass.cs:
public void OnSuccess
{
// What should I put here to call MyMethod?
}
something like this should do the trick! (I think, it been a while...)
MyClass.cs:
using System;
namespace MyClass {
public static class Program {
public static void MyMethod()
{
// [Working Code...]
}
}
}
MyActivity.cs:
using System;
using MyClass; //get your other file by the namespace
namespace MyActivity {
public static class Program {
public static void OnSuccess()
{
Program.MyMethod(); //[class name]..MyMethod();
}
}
}

Avoid Dependency cycles in Dependency Injection

C# Windows Form application.
I have an Hub and a class. Both should reference each other.
This is because:
from the hub I need to call the class' methods
from the class I need to retrieve my Hub
Right now I'm able to do the first point using Autofac:
using Autofac;
using Autofac.Integration.SignalR;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Hosting;
using Owin;
using MyProject.Classes;
using System;
using System.Reflection;
using System.Windows.Forms;
namespace MyProject
{
static class Program
{
static IDisposable webApp;
[STAThread]
static void Main()
{
string url = "http://localhost:8080";
webApp = WebApp.Start(url);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Engine());
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
var builder = new ContainerBuilder();
var config = new HubConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterType<Erp>().PropertiesAutowired().InstancePerLifetimeScope();
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.MapSignalR(config);
}
}
}
here the Hub:
using Microsoft.AspNet.SignalR;
using MyProject.Classes;
using System;
using System.Threading.Tasks;
namespace MyProject.Hubs
{
public class LiveHub : Hub
{
private readonly Erp _erp;
public LiveHub(Erp erp)
{
_erp = erp;
}
public override Task OnConnected()
{
_erp.someMethod();
return base.OnConnected();
}
}
}
and here Erp.cs:
public class Erp
{
public Erp()
{
}
public void SomeMethod()
{
// do something
}
public void SomeOtherMethod()
{
// usually I do:
var hub = GlobalHost.ConnectionManager.GetHubContext<LiveHub>();
hub.Clients.All.foo();
}
}
but here I read:
A common error in OWIN integration is use of the GlobalHost. In OWIN you create the configuration from scratch. You should not reference GlobalHost anywhere when using the OWIN integration. Microsoft has documentation about this and other IoC integration concerns here.
If I cannot use the "old" method, how should I retrieve my Hub?
I tried to add another DI in Erp for LiveHub but it doesn't work. In my form I create an instance of Erp:
public partial class Engine : Form
{
private Erp _erp = new Erp();
public Engine()
{
InitializeComponent();
}
}
if I add that DI the declaration will be impossible because I need to define the LiveHub in constructor, that requires itself the Erp parameter...
What am I not seeing?
You can decouple the Hub from the object (Erp in your case) by emitting events.
namespace MyProject.Hubs
{
public class LiveHub : Hub
{
public event Action SomethingHappened;
public override Task OnConnected()
{
SomethingHappened?.Invoke();
return base.OnConnected();
}
}
}
Now you can connect the Erp without the Hub having to know it. You will have to subscribe to the event somewhere else in your code. But the circular reference is broken.
To decouple Engine from Form you could do something like this:
public partial class EngineForm : Form
{
public EngineForm()
{
InitializeComponent();
}
}
public class Engine
{
public Engine(EngineForm form, Erp erp)
{
this.form = form;
this.erp = erp;
}
// Here is where you'll write some code to coordinate
// communication between the Erp and the EngineForm.
//
// The main advantage is that you can inject the Erp
// and have it preconfigured.
}

Class Property inside Class On Separate Thread

I have a host class which launches an instance of another class on a new thread like so:
I am referencing this MSDN article according to which, Class2.P1 should NOT be null.
LINK: http://msdn.microsoft.com/en-us/library/system.threading.threadstart.aspx
Am I missing anything obvious?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new Host().DoWork();
}
}
public class Host {
Class2Parent c = new Class2();
Thread t;
public void DoWork() {
c.P1 = new Class3();
t = new Thread(c.Start);
t.Start();
}
}
public class Class2Parent {
public Class3 P1 = null;
public virtual void Start() {}
}
public class Class2 : Class2Parent {
public Class3 P1 = null;
public override void Start() {
Console.WriteLine(P1 == null); // this is always true
}
}
public class Class3
{}
}
You can try to create a new thread using a timer variable just like that :
private Timer m_RequestTimer;
public void Begin()
{
// Timer check
if (m_RequestTimer != null)
{
m_RequestTimer.Change(Timeout.Infinite, Timeout.Infinite);
m_RequestTimer.Dispose();
m_RequestTimer = null;
}
m_RequestTimer = new System.Threading.Timer(obj => { c.Start(); }, null, 250, System.Threading.Timeout.Infinite);
}
}
where m_RequestTimer is an attribute of your class host and Begin a method of Host.
I hope it will help you =)

how to declare global function or method using c#?

Can anyone tell me how to declare a global function in c#, similar to what a Module does in VB.net?
I need to call a function that can be called in my form1, form2, and form3.
i have this code :
using System.Data.OleDb;
namespace XYZ
{
public static class Module
{
public static void dbConnection()
{
OleDbConnection con = new OleDbConnection();
con.ConnectionString = "provider= microsoft.jet.oledb.4.0;data source=..\\dbCooperative.mdb";
con.Open();
}
}
}
and form1:
using System.Data.OleDb;
using XYZ;
namespace XYZ
{
public partial class frmReports : Form
{
public frm1()
{
InitializeComponent();
}
private void frm1_Load(object sender, EventArgs e)
{
Module.dbConnection();
OleDbCommand cm = new OleDbCommand("SELECT * FROM table", con);
}
}
}
but i has an error: "The name 'con' does not exist in the current context".
If you're using C# 6.0 or later, you could use using static.
For example,
using static ConsoleApplication.Developer;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
// Global static function, static shorthand really
DeveloperIsBorn(firstName: "Foo", lastname: "Bar")
.MakesAwesomeApp()
.Retires();
}
}
}
namespace ConsoleApplication
{
class Developer
{
public static Developer DeveloperIsBorn(string firstName, string lastname)
{
return new Developer();
}
public Developer MakesAwesomeApp()
{
return this;
}
public Developer InsertsRecordsIntoDatabaseForLiving()
{
return this;
}
public void Retires()
{
// Not really
}
}
}
One more example:
using static System.Console;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
WriteLine("test");
}
}
}
You could create a static class.
namespace MyNamespace
{
public static class MyGlobalClass
{
public static void MyMethod() { ... }
}
}
You would then add the namespace in the using section of your calling class to access it. Like this:
using MyNamespace;
public class CallingClass
{
public void CallingMethod()
{
MyGlobalClass.MyMethod();
}
}
You can create a static class (even enclose it in it's own namespace so as not to pollute the main project namespace), then call it from anywhere:
namespace SomeNamespace
{
public static class SomeClass
{
public static string SomeMethod()
{
...
}
}
}
Then, in your code, you can call it using:
string x = SomeNamespace.SomeClass.SomeMethod();
Or, you can set up a using at the top of the code and just reference it without the namespace:
using SomeNamespace;
...
string x = SomeClass.SomeMethod();
#kol is right, there are NO global functions in C#. Take a look at this post MSDN post. I would use layers (I renamed your "Module" class to "TransactionsModule") and It would look like this:
using System;
using System.Collections.Generic;
using System.Data.OleDb;
namespace XYZ
{
public class TransactionsModule
{
public List<Person> GetPersons(string query, string connectionString)
{
List<Person> dbItems = new List<Person>();
OleDbConnection conn = new OleDbConnection(connectionString);
try
{
conn.Open();
var cmd = new OleDbCommand(query, conn);
cmd.CommandText = query;
using (OleDbDataReader reader = cmd.ExecuteReader())
{
Person objPerson = new Person();
//These are the columns returned
objPerson.Name = Convert.ToString(myReader["Name"]);
objPerson.Age = Convert.ToInt32(myReader["Age"]);
dbItems.Add(objPerson);
}
}
catch(OleDbException ex)
{
throw ex;
}
finally
{
conn.Close();
}
return dbItems;
}
}
//This class should be in another Layer, but I placed it here since It's a quick Example
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
All the logic was abstracted to the TransactionsModule class, then you only need to call the Method: GetPersons. Take a look:
using System;
using System.Collections.Generic;
using XYZ.TransactionsModule;
namespace XYZ
{
public partial class frmReports : Form
{
public frm1()
{
InitializeComponent();
protected TransactionsModule moduleTran;
}
private void frm1_Load(object sender, EventArgs e)
{
//We initialize the Data Access Layer class
moduleTran = new TransactionsModule();
//This ConnectionString should be in your app.config
string conString = "provider= microsoft.jet.oledb.4.0;data source=..\\dbCooperative.mdb";
string sqlQuery = "SELECT * FROM table";
List<Person> ItStaff = moduleTran.GetPersons(sqlQuery, conString);
}
}
}

Categories

Resources