I've seen a couple examples out there that could possibly help me, but I don't have that much time to explore them as I just found out today that my bosses have to demo this a week earlier than planned, and I want to add this new functionality. I'll try and keep this short and sweet.
Ok, this is like my 10th time trying to right this to make it clear, hopefully it is. This is an application. Rows of data need to be displayed in a DataGridView (done). Some rows are highlighted differently based on reports (done). Most reports have their own SQL file and are implemented at runtime from an INI file (done). However, some reports need to call a Function. The application is using an SQLite database. I would like to have DLLs that are reports, all of the same format, and all of them return a List of ReportRecord. ReportRecord is a class defined in my main application but I would also define it in each DLL when they are created. I want to instantiate the DLL, call it's "GetRecords" function, and use it in my main application. Here is some psuedocode. If you guys can tell me if it's possible, or give me an idea of a better way to do this, I'd appreciate it.
PSUEDOCODE
foreach (string str in System.IO.Directory.GetFiles("C:\\ReportDlls", "*.dll"))
{
//Instantiate DLL e.g. newReport
//_lstReportRecords.AddRange(newReport.GetReportRecords());
}
Is there anyway to do this?
Currently, I have the following to supplement until I find this out:
private void RefreshReports(string strReportTitle)
{
_lstReportRecords = _lstReportRecords.Where(rr => rr.Description != strReportTitle).ToList<ReportRecord>();
string strColumn = iniFile.GetString(strReportTitle, "Column", "");
if (strColumn != null)
{
_lstReportRecords.AddRange(_dataController.BuildReportList(strColumn, strReportTitle, GetReportSQL(strReportTitle)));
}
else
{
switch (strReportTitle)
{
case "Improper Indenture":
_lstReportRecords.AddRange(_dataController.ImproperIndenture());
break;
case "Skipping Figure":
_lstReportRecords.AddRange(_dataController.SkippingFigure());
break;
default: break;
}
}
FormatCells();
}
Thanks everyone.
Edit: Sorry guys, looking at that stuff is honestly making me feel stupid. Like, my mind is going blank and all and can't concentrate on it. :) What you guys have provided is probably the best way, but since I have to have a quality Demo ready by Tuesday and there shouldn't be any more reports added needing functions until then, I'm going to keep this open. Once my boss is out of town to demo it, I'll work on implementing this. But right 开发者_开发知识库now, it's going to go unanswered unless I see an example that is very very (for 2 year olds) straight forward.
You can simply create a C# library project implementing the interfaces below and store the binary file in the database or on the file system. You could then load the assembly from raw assembly bytes/file path an instantiate an object. With reflection you can also call the constructor directly, but i prefer the factory pattern for such tasks.
public interface IReportModule
{
}
public interface IReportModuleFactory
{
IReportModule Create();
}
private static IReportModule CreateReportModuleFromRawAssemby(byte[] rawAssembly)
{
var reportModule = Assembly.Load(rawAssembly);
var factoryType = reportModule.GetExportedTypes()
.FirstOrDefault(x => x.IsAssignableFrom(typeof(IReportModuleFactory)));
if (factoryType != null)
{
var reportModuleFactory = (IReportModuleFactory)
reportModule.CreateInstance(factoryType.FullName);
return reportModuleFactory.Create();
}
else
throw new NotImplementedException("rawAssembly does not implement IReportModuleFactory");
}
Don't look at this in terms of DLL's, which are the raw files, but Assemblies, which is how .NET sees things. You can load an Assembly using Assembly.Load
. Having said this, have you considered a more generic solution, such as inversion of control?
I'm not really understanding what exactly you're trying to do, and IOC is probably the way to go here. But from what I understand, you could do this with pure reflection.
Mind you, this is far from the ideal way of doing things like this, but you're asking for it :)
Here it goes (top of my head, so don't shoot me if anything is wrong, it should be pretty close, though not foolproof)
// load assembly
var assemblyWithReport = Assembly.LoadFrom("Path of your assembly");
// or another Loadxx to get the assembly you'd
// like, whether it's referenced or not
// load type
var reportType = assemblyWithReport.GetTypes().ToList()
.Where(t => t.Name == "ReportRecord").Single();
// create instance of type
var instance = Activator.CreateInstanceOf(reportType);
// get getrecords method of the type
var getRecordsMethod = reportType.GetMethod("GetRecords");
// invoke getrecords method on the instance
object result = getRecordsMethod.Invoke(instance, null);
Consider a plugin architecture, such as the Managed Extensibility Framework, for managing your report modules.
You may want to consider using the Managed Extensibility Framework for this. It makes this type of operation trivial.
You can download it above for .NET 3.5 (it's in the framework in .NET 4 already). By using MEF, you could just import the collection of all exported "reports" in one shot via a DirectoryCatalog, and it will take care of all of the wiring for you.
For details, see the help on Importing Collections here.
精彩评论