Reflection instead .NET-to-.NET scenario - c#

I created Person.dll and register it(regsvcs.exe) in Command Promt for Visual Studio 2019. As a result of registration, I got Person.tlb. I tried to add Person.tlb in console project as reference COM component but I got warning MSB3290.
warning MSB3290: Failed to create the wrapper assembly for type
library "{8b1098cb-d453-4dc7-96ac-52df54d0a2ce}". Type library
'Person' was exported from a CLR assembly and cannot be re-imported as
a CLR assembly.
How I can to add Person.tlb in console project using reflection?
Person.dll:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
namespace COM
{
[ClassInterface(ClassInterfaceType.None)]
public class Person : ServicedComponent, COM.IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsMale { get; set; }
public void Persist(string FilePath)
{
StreamWriter oFile = new StreamWriter(FilePath);
XmlSerializer oXmlSerializer = new XmlSerializer(typeof(Person));
oXmlSerializer.Serialize(oFile, this);
oFile.Flush();
oFile.Close();
}
static public Person Retrieve(string FilePath)
{
StreamReader oFile = new StreamReader(FilePath);
XmlSerializer oXmlSerilizer = new XmlSerializer(typeof(Person));
Person oPerson = oXmlSerilizer.Deserialize(oFile) as Person;
return oPerson;
}
}
}
Console project:
using System;
namespace Test10
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
COM.Person per = new COM.Person();
per.FirstName = "Maxim";
per.LastName = "Donax";
per.Persist(#" C:\myFile.xml ");
}
}
}

I used other way: created Person.dll in Visual Studio and registerd it(regsvcs.exe). After use reference Person.tlb in Visual Basic 6.0.

Related

Injecting multiple instances of a class with settings

I'm having a bit of trouble with using Azure storage. I have an implementation at the moment which is fine but I want to expand it so that I am able to use multiple storage accounts/containers in one solution. I can't get my head around how to do that and still allow for dependency injection. I also need to be able to pass in settings which define the connection string and container name
This is how I'm doing it at the moment:
builder.Services.AddSingleton<IAzureStorageClient, AzureStorageClient>();
builder.Services.Configure<AzureStorageSettings>(configuration.GetSection("AzureStorageSettings"));
and then in the constructor
public AzureStorageClient(IOptions<AzureStorageSettings> options)
{
var azureStorageSettings = options.Value;
var cloudStorageAccount = GetCloudStorageAccount(azureStorageSettings.ConnectionString);
_blobClient = cloudStorageAccount.CreateCloudBlobClient();
_blobContainer = GetBlobContainer(azureStorageSettings.ContainerName);
}
I've read a lot of similar posts which mention using named registrations but I am using the built in IoC container and it doesn't allow for that. I've also seen posts saying to use a factory which looks good but I am hoping to package this logic and share it among different solutions and the factory solution would require a lot of configuration which I would like to avoid to make the package easy to consume.
Update:
I made the settings an interface to force it to be implemented each time it is required and I used the generic T to pass that into my storage client as follows:
public sealed class AzureStorageClient<T> : IAzureStorageClient<T> where T : class, IAzureStorageSettings, new()
public AzureStorageClient(IOptions<T> options)
{
var azureStorageSettings = options.Value;
var cloudStorageAccount = GetCloudStorageAccount(azureStorageSettings.ConnectionString);
_blobClient = cloudStorageAccount.CreateCloudBlobClient();
_blobContainer = GetBlobContainer(azureStorageSettings.ContainerName);
}
Then it could be injected like this:
builder.Services.AddSingleton<IAzureStorageClient<SpecificStorageSettings>, AzureStorageClient<SpecificStorageSettings>>();
Just an example, you can use settings like this:
Settings.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace UseDifferentSettings
{
public abstract class Settings
{
public abstract string connectingstring {
get;
}
public abstract string containername {
get;
}
public abstract string blobname {
get;
}
}
}
Setting1.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace UseDifferentSettings
{
class Setting1 : Settings
{
string _connectingstring = "DefaultEndpointsProtocol=https;AccountName=xxx;EndpointSuffix=core.windows.net";
string _containername = "video1";
string _blobname = "test.txt";
public override string connectingstring
{
get { return _connectingstring; }
}
public override string containername
{
get { return _containername; }
}
public override string blobname
{
get { return _blobname; }
}
}
}
Setting2.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace UseDifferentSettings
{
class Setting2 : Settings
{
private string _connectingstring = "DefaultEndpointsProtocol=https;AccountName=xxx;EndpointSuffix=core.windows.net";
private string _containername = "test";
private string _blobname = "test.txt";
public override string connectingstring
{
get { return _connectingstring; }
}
public override string containername
{
get { return _containername; }
}
public override string blobname
{
get { return _blobname; }
}
}
}
UploadToStorage.cs
using Azure.Storage.Blobs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace UseDifferentSettings
{
public class UploadToStorage
{
Settings setting;
public UploadToStorage(Settings setting) {
this.setting = setting;
}
public void GoUpload() {
string connectingstring = setting.connectingstring;
string containername = setting.containername;
string blobname = setting.blobname;
string filecontent = "This is my test file content";
byte[] array = Encoding.ASCII.GetBytes(filecontent);
MemoryStream filestream = new MemoryStream(array);
BlobServiceClient blobServiceClient = new BlobServiceClient(connectingstring);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containername);
BlobClient blobClient = containerClient.GetBlobClient(blobname);
blobClient.Upload(filestream);
}
}
}
Program.cs(The main method class)
using System;
namespace UseDifferentSettings
{
class Program
{
static void Main(string[] args)
{
Settings setting1 = new Setting1();
Settings setting2 = new Setting2();
UploadToStorage uploadtostorage = new UploadToStorage(setting1);
uploadtostorage.GoUpload();
Console.WriteLine("Hello World!");
}
}
}

WCF service return base class only

I'm working on a WCF service and trying to cleanup code. Issue I'm running into is I have a base data type I want to send as results from the service to the client; however in the service code itself it will mostly make use of classes derived on the base with additional properties for processing. The client has no reason to know about these at all.
Right now I can get this working but only if I define the derived classes in the shared library. I do not want them in there as they are specific solely to the service.
Below is example to show the problem. All three files are in separate projects in the same solution.
Common.IPersonService.cs
using System.ServiceModel;
using System.Runtime.Serialization;
namespace Common
{
[ServiceContract]
public interface IPersonService
{
[OperationContract(Name = "GetPersonById")]
Person GetPersonById(int id);
}
[DataContract(Name = "Person")]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
}
WcfClient.Program.cs
using System;
using System.ServiceModel;
using Common;
namespace WcfClient
{
class Program
{
static void Main(string[] args)
{
var binding = new NetTcpBinding();
var endpoint = new EndpointAddress("net.tcp://localhost:8001/WcfTest/");
var factory = new ChannelFactory<IPersonService>(binding, endpoint);
IPersonService service = null;
try
{
service = factory.CreateChannel();
Person result = service.GetPersonById(5);
Console.WriteLine(result.Name);
((ICommunicationObject)service).Close();
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
}
}
WcfService.Program.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using Common;
namespace WcfService
{
[DataContract(Name = "Person")]
public class Contact : Person
{
public string Address { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(PersonService)))
{
serviceHost.Open();
Console.WriteLine("Service started");
Console.ReadLine();
}
}
}
public class PersonService : IPersonService
{
private Dictionary<int, Contact> _testData = new Dictionary<int, Contact>();
public PersonService()
{
Random rnd = new Random();
for (int i = 0; i < 100; i++)
{
_testData.Add(i + 1, new Contact()
{
Id = i + 1,
Name = Guid.NewGuid().ToString(),
Address = Guid.NewGuid().ToString()
});
}
}
public static void Configure(ServiceConfiguration config)
{
config.AddServiceEndpoint(typeof(IPersonService), new NetTcpBinding(), "net.tcp://localhost:8001/WcfTest/");
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
public Person GetPersonById(int id)
{
return _testData[id];
}
public Person GetValueByKey(string key)
{
return null;
}
}
}
The exception received is the following:
The socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was '00:00:59.9780000'.
Now if I move the Contact class from the WcfService project and put it in the Common project it will work. As said though I'd prefer not to muddy a common library with items specific to the service implementation.
Thanks!

When do we need [OptionalField]?

I have a serializable class in some library assembly.
also I have to projects referencing this assembly.
One project serializes an instance of my class,
other project serializes this instance of my class.
I copied dll of this assembly into some backup folder to get ability to restore an old version.
I added new field in my class.
I serialized an instance of my updated class.
Then I restored old version of my class and deserialized the instance.
BinaryFormatter works well and doesn't throw an exception.
Do we need an [optionalField] attribute?
Library
using System;
namespace SerializationFramework
{
[Serializable]
public class MyClass
{
public string Field { get; set; }
//public string NewField { get; set; }//added in Version 2, without [OptionalField] attribute
public MyClass()
{
Field = "test";
//NewField = "newField";
}
//for checking versions
public void Test()
{
Console.WriteLine("Version 1");//comment after adding a new field
//Console.WriteLine("Version 2");//uncomment after adding a new field
Console.ReadLine();
}
}
}
Serializing
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using SerializationFramework;
namespace Serialization
{
class Program
{
static void Main(string[] args)
{
var binaryF = new BinaryFormatter();
var obj = new MyClass();
using (var f = File.Create(#"C:\temp\oleg.txt"))
{
binaryF.Serialize(f, obj);
}
Console.WriteLine("Serialized!");
Console.ReadLine();
}
}
}
Deserializing
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using SerializationFramework;
namespace Deserialization
{
class Program
{
static void Main(string[] args)
{
var binaryF = new BinaryFormatter();
MyClass obj;
using (var f = File.Open(#"c:\temp\oleg.txt", FileMode.Open))
{
obj = (MyClass) binaryF.Deserialize(f);
}
//CheckingVersion
var obj2 = new MyClass();
obj2.Test();
}
}
}

How to get the app version from ContentPage?

I tried this: iPhone MonoTouch - Get Version of Bundle
NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleVersion").ToString();
But this didn't work. As NSBundle can't be found.
How can I get the app version (iOS and Android) from ContentPage?
The code which i ended up with (thanks to Steven Thewissen):
PCL (shared code)
using System;
namespace MyApp.Interfaces
{
public interface IApplicationVersion
{
string ApplicationsPublicVersion { get; set; }
string ApplicationsPrivateVersion { get; set; }
}
}
Android
using System;
using MyApp.Droid.Helpers;
using MyApp.Interfaces;
using Xamarin.Forms;
[assembly: Dependency(typeof(ApplicationVersion))]
namespace MyApp.Droid.Helpers
{
public class ApplicationVersion : IApplicationVersion
{
public string ApplicationsPublicVersion { get; set; }
public string ApplicationsPrivateVersion { get; set; }
public ApplicationVersion()
{
var context = Android.App.Application.Context;
var info = context.PackageManager.GetPackageInfo(context.PackageName, 0);
ApplicationsPublicVersion = info.VersionName;
ApplicationsPrivateVersion = info.VersionCode.ToString();
}
}
}
iOS
using System;
using MyApp.Interfaces;
using MyApp.iOS.Helpers;
using Foundation;
using Xamarin.Forms;
[assembly: Dependency(typeof(ApplicationVersion))]
namespace MyApp.iOS.Helpers
{
public class ApplicationVersion : IApplicationVersion
{
public string ApplicationsPublicVersion { get; set; }
public string ApplicationsPrivateVersion { get; set; }
public ApplicationVersion()
{
ApplicationsPublicVersion = NSBundle.MainBundle.InfoDictionary[new NSString("CFBundleShortVersionString")].ToString();
ApplicationsPrivateVersion = NSBundle.MainBundle.InfoDictionary[new NSString("CFBundleVersion")].ToString();
}
}
}
You can do this by implementing a Dependency Service. First you define an interface in your shared code:
namespace MyApp
{
public interface IAppVersionProvider
{
string AppVersion { get; }
}
}
In each platform project you then implement the interface.
iOS
[assembly: Dependency(typeof(AppVersionProvider))]
namespace MyApp.iOS
{
public class AppVersionProvider : IAppVersionProvider
{
public string AppVersion => NSBundle.MainBundle.InfoDictionary[new NSString("CFBundleVersion")].ToString();
}
}
Android
[assembly: Dependency(typeof(AppVersionProvider))]
namespace MyApp.Droid
{
public class AppVersionProvider : IAppVersionProvider
{
public string AppVersion
{
get
{
var context = Android.App.Application.Context;
var info = context.PackageManager.GetPackageInfo(context.PackageName, 0);
return $"{info.VersionName}.{info.VersionCode.ToString()}";
}
}
}
}
You can then retrieve the version number from shared code through:
var version = DependencyService.Get<IAppVersionProvider>();
var versionString = version.AppVersion;
If you don't want to use dependency services, you can just use the class VersionTracking.
The property VersionTracking.CurrentVersion will give you the Version you can set in your Android properties and your iOS info.plist.
This class is provided by Xamarin.Essentials and can give you a lot of informations. Please, check the documentation here for more informations.
Edit: listed incorrect nuget package, changes made below.
You should in theory be able to use something like the below inside the OnStart(); method of your App.cs in your forms project.
Context context = this.ApplicationContext;
SupportFunctions.Version = context.PackageManager.GetPackageInfo(context.PackageName, 0).VersionName;
However we use a plugin created by Mark Trinder called "Xam.Plugin.Version" which can be found on nuget1 and on GitHub2. Once it's installed into your forms & native projects it's simply called as so:
using Version.Plugin;
private void SomeMethod()
{
MyLabel.Text = CrossVersion.Current.Version;
}
1 nuget package Here
2 Github Here :

C# and VBScript Interoperability - Forms

I am essentially trying to call a C# form from a vbscript. I have been able to get the interoperability to work on non-forms. The following works:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using InteropTestingForm;
[assembly:System.CLSCompliant(true)]
[assembly: ComVisible(true)]
[assembly:Guid("a22f4018-8f32-4c02-a748-6701fb617aa3")]
namespace InteropTesting
{
[Guid("a22f4018-8f32-4c02-a748-6701fb617aa3")]
public class TestReply
{
public string salutation;
public string name;
public string time;
}
[Guid("a22f4018-8f32-4c02-a748-6701fb617aa4")]
public class TestObj
{
public TestObj() { }
public TestReply SayHello(string addressee)
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(Form1());
return SayHello(addressee, "hello");
}
public TestReply SayHello(string addressee, string greeting)
{
string x = String.Format("{0}, {1}!", greeting, addressee);
Console.WriteLine("{0}", x);
TestReply r = new TestReply
{
salutation = greeting,
name = addressee,
time = System.DateTime.Now.ToString("u")
};
return r;
}
}
}
As can be seen in my SayHello() function, I want to run another form, possibly in the same namespace. I am not sure how to accomplish this. I keep getting the following error: "The type or namespace name Form1() could not be found (are you missing a using directive or an assembly reference)? when I try to run the following command from a Visual Studio command prompt:
csc.exe /t:library /debug+ /keyfile:InteropTesting.snk /out:InteropTesting.dll TestObj.cs

Categories

Resources