I have 2 completely independent projects one in vb and one in c#
Project A - VB
Project B - C#
We have scenario where by code in project A is currently using code in project B
e.g
Project A
Public Class SMS
Public Sub New()
End Sub
Public Function SendSMS() As String
Return "SMS Sent "
End Function
End Class
Project B
using ProjectA;
public class Email
{
public string SendEmail()
{
return "Email Sent ";
}
public string SendEmail(SMS sms)
{
sms.SendSMS();
return "Email Sent ";
}
}
This works fine However we now have a requirement to Send an Email from Project A which would create a circular reference After reading loads of help on here I created an interface and used the interface in project a as such:
Project C
Public Class Interface
Public Interface IEmail
Sub SendEmail()
End Interface
End Class
I have implemented the interface on Email and changed Project A to :
Public Function SendSMS(ByVal tEmail As IEmail) As String
tEmail.SendEmail()
Return "SMS Sent "
End Function
Which works fine from my ui as I can instantiate the object and all works fine.
However I have run into an issue where by I don’t want to instantiate the bject in the UI but instead in the Project A
Public Sub SendSMSAndEMail()
Dim temail As New email
Me.SendSMS(temail)
End Sub
I have hit a real road block? i think i should be looking at a factory class but i am getting confused as to how best to achieve this?
N.B Further info:
If we were to Design the system again project A and Project B would be in the same project (and Language) and this would not cause an issue. As it is we have 2 UI's A Console server app and a Win forms UI. Project B was originally for the console app and uses various functions that the main Win form app uses. It’s now arisen that there is code in the console app that we require from within the main app such as the ability to Send An E-mail however the code would be difficult to extract from the console app as it’s in a different language to the main application.
Evil solution: Static Factory Function
Declare this static class in your root project (example in interface project).
public static class EmailFactory{
public static Func<IEmail> Create{ get;set; }
}
Then in your UI (I assume that your Project will end up starting in UI), set it like:
EmailFactory.Create = new Func<IEmail>( () => { return new ProjectB.Email() } );
This way you can use it statically from Project A (even you still need to execute it from UI).
But forget it, you won't need it. Or at least, don't use it.
If you said that
The Current Sending process of SMS messages is Handled within Project A.
Does it means that Project A can be executed directly? I hope not. A library being executed is kind of evil for me. If you can, make the execution in separate project, and that project can reference to Project A and B as well. If so, then your interface approach is already fulfill the requirement.
EDIT:
Looks like the Project A or B itself is at UI level. It is a bad design to has library code inside UI project. However it will take months to refactor the whole application.
What I can suggest is to create a layer of library, which need to be used interactively between the project. So the architecture shall be:
infrastructure
|
|
library
/ \
/ \
[Project A] [Project B]
IMHO this is the only way to fix your problem.
Related
I've struggle several hours on that and I can't find what I'm doing wrong.
I created a new C# dll project, here is the content of the only class it contain:
using System;
using System.Runtime.InteropServices;
namespace PolygonSl {
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Config {
[ComVisible(true)]
public string GetCompany() {
return "POL";
}
}
}
I basically remove everything from it trying to make it work, the only reference is System.
I checked the Make assembly COM-Visible flag on the Assembly Information and my project is signed (seams required for codebase).
It compiling fine, after that, I called RegAsm.exe, giving it my dll, I added /codebase and /tlb, the command is successful.
When I go to my VBA project, I can add my new tlb file to the references, working fine. After, I can use it in my code, the autocomplete is working and I can compile with no errors.
Then, when I execute, I got this:
Run-time error '430':
Class does not support Automation or does not support expected interface
Here is my code sample in the VBA:
Private Sub Button1_Click()
'With CreateObject("PolygonSl.Config")
With New PolygonSl.Config
MessBox .GetCompany, MB_OK, "Test"
End With
End Sub
I tried late binding and my code is running fine with it but I'd like to be able to use the autocomplete.
Anyone have a suggestion on what I could try to make it work?
Edit (Adding some details on my environment)
I work on VS2008 for projects related to Dynamics SL (one of the Microsoft ERPs)
I'm on Windows Server 2008 R8 Standard, running from VMWare
Compiling on Framework 3.5, Release, x86, Dynamics SL client is 32 bits
I tried my dll on Dynamics but also on Excel to be sure that the problem was not Dynamics ;)
I think you need to define an interface to be able to see getcompany.
using System;
using System.Runtime.InteropServices;
namespace PolygonSl
{
[Guid("6DC1808F-81BA-4DE0-9F7C-42EA11621B7E")]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IConfig
{
string GetCompany();
}
[Guid("434C844C-9FA2-4EC6-AB75-45D3013D75BE")]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.ClassInterface(ClassInterfaceType.None)]
public class Config : IConfig
{
public string GetCompany()
{
return "POL";
}
}
}
You can generate the interface automatically by placing the cursor in the class definition and using Edit.Refactor.ExtractInterface.
I'd have to admit that I'm at the absolute edge of my abilities here and the above is put together based on examples I've seen elsewhere.
Edit
The following test code works fine on my PC
Option Explicit
Sub polygontest()
Dim my_polygon As SOPolygon.Config
Set my_polygon = New SOPolygon.Config
Debug.Print my_polygon.GetCompany
End Sub
Where SOPolygon is the project name.
I recently started working on a Xamarin Android/iOS project with a PCL class where I want to put all the logic in. Like my Refit interfaces, ReactiveUI ViewModels et cetera, but every time when trying to execute my code I get a error saying that my interface is not a Refit interface. Currently my interface looks like this.
public interface IMyApi
{
[Post("/authenticate")]
IObservable<Models.ApiResponses.AuthenticationResponse> SigninRaw([Body] JObject credentials);
[Get("/service")]
IObservable<Models.ApiResponses.MyListResponse> GetServiceListRaw();
[Get("/service/{id}/idstatus")]
IObservable<Models.ApiResponses.IdResponse> GetIdStatusRaw(string Id);
}
As far as I know this looks good and this also works when I'm trying to load this from a specific platform like iOS project. But when trying to do it from a PCL if fails! I have installed the Refit package in both of my platform specific project Android & iOS and I referenced a dll in the PCL, what did I miss?
If there is need for more information or you have any question, please do not hesitate to ask.
Well without further ado, thank you for reading and hopefully someone can assist me with this, because I starting to loose my mind the past couple of days.
Edit: added calling method.
Here I calling it from a ViewModel
var client = new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://api.address.com")
};
var api = RestService.For<IMyApi>(client); <= here it crashes
var response = api.SigninRaw(token);
I've managed to track this down, there are actually a few issues at play. Fortunately there are ways to work around them.
The first problem is that the PCL interfaces aren't being detected in the first place. Refit runs a utility at compile time (InterfaceStubGenerator) which scans the subfolders for interface classes and generates implementation code for each one. These are all packed into an intermediate file called RestStubs.g.cs which gets included in with the assembly. This utility, however, is only run on the projects that Refit has been added to via nuget, and since that doesn't include PCL projects the interfaces in those projects never get processed. The solution is to call this utility manually in a pre-build step and include the generated file in each of the platform projects. Go to your PCL project's property settings and add the following to the pre-build steps:
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.Droid\RefitStubs.cs ..\..\..\ProjName
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.iOS\RefitStubs.cs ..\..\..\ProjName
That will generate RefitStubs.cs for your platform projects, so add each file to it's respective project.
Ordinarily that would be the end of it were it not for another problem. The RestService.For<> generics that you call to get the implementations make the assumption that the implementation classes reside in the same assembly as their corresponding interfaces. Obviously that isn't the case for PCL projects. To get around this you need to implement your own version of the RestService class, this will probably serve most of your needs:
public static class PclRestService
{
public static T For<T>(string hostUrl)
{
var className = "AutoGenerated" + typeof(T).Name;
var typeName = typeof(T).Namespace + "." + typeof(T).Name.Replace(typeof(T).Name, className);
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var generatedType = assembly.GetType(typeName);
var requestBuilder = RequestBuilder.ForType<T>();
var client = new HttpClient(new HttpClientHandler()) { BaseAddress = new Uri(hostUrl) };
return (T)Activator.CreateInstance(generatedType, client, requestBuilder);
}
}
You then call it like so:
var netService = PclRestService.For<IMyApi>("http://jsonplaceholder.typicode.com");
var result = await netService.GetDataOrSomething();
One last problem you may encounter is multiple declarations of the PreserveAttribute class, which is declared at the top of the stub files. So long as you don't add Refit interfaces to your platform projects this shouldn't happen, but if it does then you have 3 options:
modify the InterfaceStubGenerator utility to not create that code
write a pre-processor to strip that code out once it has been generated
go to Refit's "refit.targets" file and comment-out the line "<Exec Command="$(RefitExecCmd)" />
The Refit tools folder includes the template file used to generate stub files but for some strange reason it's ignored altogether and statically linked to the application, so editing it in the tools folder doesn't do anything at all.
I am creating a class library and have different functions inside. I also have a Console application that can access this functions once they reference the class library. I would like to know how to make a function "invisible" so the client won't be able to see it exist and they can only use it if they write it out perfectly.
I have this function in my class Library :
public string custommessage(string messagetosend)
{
string receivedmessage = CallServer(messagetosend);
return receivedmessage;
}
and basicly when I am in a different program referencing the library I dont want to see this function in my list of avaiable functions to chose from :
Append
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
to your method as an attribute. This will hide the function from intellisense.
I'm trying to establish the feasibility of using our company's product with COM. To do this, I've written a (MyAppCom) dll within the solution and referenced the original (MyApp)exe project. MyAppCom creates and instance of the MyApp (mainform).
The main problem I am coming up against is that the application entry point is an application type (with settings, startup forms, etc.). Generally, this isn't so bad, but there are a couple issues that I'm banging my head against.
The app config uses the calling exes name to search for the application configuration file (i.e. if I'm calling a com instance of MyApp via Python, its looking for python.exe.config instead of MyApp.exe.config. I've sidestepped this issue for the moment by just copying the settings file, but if anybody knows how to reference this specific config settings, any help would be appreciated.
The second point is a bit more sticky. It seems that MyApp has set mainform as its startup form, which seems to create a global instance of it. I've looked everywhere and there's no explicit initializations of mainform except through MyAppCom. This becomes a problem when I initialize mainform from MyAppCom and child forms are trying to reference the global MyApp.mainform. In this case, it is obviously not initialized (since I didn't create it from MyApp).
Is this something other people have done? I've searched for a couple days now with no luck but I don't imagine I'm one of few who've tried. A little background on the product - it was originally written in VB6 and now is updated to VB.NET with new projects in C#. The wrapper is also written in C#.
Here is the com wrapper (MyAppCom) (names have been changed to protect the not-so-innocent):
public interface IMainCom
{
void Init();
}
[ClassInterface(ClassInterfaceType.None)]
public class MainCom : IMainCom
{
private MDIMain mMDIMain = null;
public void Init()
{
OpenMain();
}
private void OpenMain()
{
mMDIMain = new MDIMain();
mMDIMain.Show();
}
}
Here also is the App.Designer for MyApp:
Namespace My
Partial Friend Class MyApplication
<Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
Public Sub New()
MyBase.New(Global.Microsoft.VisualBasic.ApplicationServices.AuthenticationMode.Windows)
Me.IsSingleInstance = false
Me.EnableVisualStyles = false
Me.SaveMySettingsOnExit = true
Me.ShutDownStyle = Global.Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses
End Sub
<Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
Protected Overrides Sub OnCreateMainForm()
Me.MainForm = Global.MyApp.MDIMain
End Sub
End Class
End Namespace
VS2012 for desktop .net framework 4.5 normal windows forms applications, not WPF
Hello, I tried to search for an answer, but I'm not sure of the correct terminology. I've managed to break my code, and can't understand what I've done wrong. (i didn't think i had changed anything, but ...)
I have a solution which contains 2 projects. The first project is an executable program, and the second is a DLL, which is loaded at run time and used by the first project.
the first project contains a form, and a static class with public static strings in the same namespace. (and some other unconnected classes). specifically:
namespace project1_namespace
{
static class settings
{
public static string some_words = "some words in a string";
}
class dll_callback{
//.. some public methods here
}
dll_callback dllcallback; // instance is initialised in the code (not shown)
Form form;
public partial class frm_splash : Form
{
private void frm_splash_FormClosing(object sender, FormClosingEventArgs e)
{
// this function actually loads the DLL, ensuring its the last step
//... some error checking code removed for brevity
Assembly assembly = Assembly.LoadFrom("c:\dllpath\project2.dll");
Type type_init = assembly.GetType("project2_class");
object init = Activator.CreateInstance(type_init, form, dllcallback);
//... some error checking code removed for brevity
}// end method
}// end form class
}// end namespace
when the form is closing, the method shown above is called which calls the second projects class project2_class constructor.
in project 2, the DLL, there is:
namespace project2_namespace
{
// how did i get this working to reference "settings" class from project 1??
public class project2_class
{
public project2_class(project2_namespace.Form1 form_ref, object callback)
{
settings.some_words = "the words have changed";
//... some more stuff
}
}
}
Now, i was experimenting with some code in an entirely different part of project2, and VS2012 suddenly started refusing to compile stating:
error CS0103: The name 'settings' does not exist in the current context
the standard solution to this appears to be to add a reference to project2, but that would create circular dependencies because project 1 calls 2 as a DLL.
I really honestly don't think i had changed anything relevant to this, but also clearly I have.
looking at it, i cant see how project 2 would have access to a class in project 1 without a reference, but the list of arguments to the project2_class constructor doesn't include one, and I am absolutely positive that it hasn't changed (and I cant change it for backwards compatibility reasons).
would really appreciate help with this, as its been a lot of work to get this working.
as a side note, I've definitely learned my lesson about not using source control. and not making "how this works" comments instead of "what this does" comments.
may dynamic help you? You can not get the setting string at complie time.