Why can NHibernate create a proxy for classes with properties that have private setters but not for classes with properties that have internal setters? Or am I missing something that should be completely obvious?
public class PrivateSetter {
// no proxy error
public virtual string PrivateSetterProperty { get; private set; }
}
public class InternalSetter {
// proxy error
public virtual string InternalSetterProperty { get; internal set; }
}
You need to mark the setter as protected internal so that the proxy class can access it:
public class InternalSetter
{
public virtual string InternalSetterProperty { get; protected internal set; }
}
This is a pure .NET language problem. Try it yourself:
public class A
{
public virtual string PrivateSetter { get; private set; }
public virtual string InternalSetter { get; internal set; }
}
in another assembly:
public class B : A
{
// works fine, private isn't derived at all
// you can omit the setter, make it public, internal to this
// assembly etc.
public override string PrivateSetter { get; set; }
// compilation time error: setter can't be overridden,
// there is no access to it.
public override string InternalSetter { get; internal set; }
}
By the way, I'm just analyzing an issue with private setters and proxies, so I'm not sure if the proxies really work in this case.
Related
Code
Web Api Interface
I now have an interface
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController: ControllerBase
{
private readonly ICustomSession _session;
public TestController(ICustomSession session)
{
_session = session;
}
public async Task<CustomResult<string>> Search(TestSearch testSearch)
{
return new CustomResult<string>(string.Empty);
}
}
My CustomSession
public interface ICustomSession
{
public int SessionId { get; set; }
public Guid UserId { get; set; }
public string UserName { get; set; }
}
My TestSearch
public class SessionInfo
{
public Guid UserId { get; set; }
public string UserName { get; set; }
}
public class TestSearch: SessionInfo
{
public string Keyword { get; set; }
}
Problem
ICustomSession can only be used in the Controller layer, can not go deep into the business, but the business is the need for SessionInfo related information, and SessionInfo is actually read from the ICustomSession.
Now I need to inherit SessionInfo on the parameter class to handle it, but now I don't want to assign the value manually in each api interface
testSearch.UserId = _session.UserId;
I want to use an automatic assignment method that detects the class that inherits SessionInfo and automatically assigns the value from ICustomSession to it
Translated with www.DeepL.com/Translator (free version)
I want to use an automatic assignment method that detects the class that inherits SessionInfo and automatically assigns the value from ICustomSession to it
I have an option class which like the following
public class EmailOptions
{
public EmailOptions(IEmailConfiguration account) {
this.Configuration = account;
}
public string DefaultFromAddress { get; set; }
public string DefaultFromDisplayName { get; set; }
public IEmailConfiguration Configuration { get; }
}
The IEmailConfiguration interface is there because in some cases I can have an Smtp library and so I need an Smtp based configuration while in some other cases I can use other services which needs a different configuration. Example:
public class ApiKeyConfiguration : IEmailConfiguration
{
public ApiKeyConfiguration() {
}
public string AccountName { get; set; }
public string AccountKey { get; set; }
}
or
public class SmtpConfiguration : IEmailConfiguration
{
public SmtpConfiguration() {
}
public string Host { get; set; }
public string Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public bool EnableSsl { get; set; }
public bool UseDefaultCredentials { get; set; }
}
I am sure I am registering the correct implementation with
services.AddTransient<IEmailConfiguration, ApiKeyConfiguration>();
However when I try to inject an IOption<> into a controller I am getting the following error:
[13:56:26 ERR] An unhandled exception has occurred: Cannot create instance of type 'EmailOptions' because it is missing a public parameterless constructor.
System.InvalidOperationException: Cannot create instance of type 'EmailOptions' because it is missing a public parameterless constructor.
at Microsoft.Extensions.Configuration.ConfigurationBinder.CreateInstance(Type type)
Of course I can add a parameterless constructor to the class but how do I ensure that the DI container will create an instance of my class by using the constructor with parameter dependency?
Technically, you may try to create own implementation of IConfigureOptions.
Represents something that configures the TOptions type. Note: These are run before all IPostConfigureOptions.
So do something like this:
public class ConfigureEmailOptions : IConfigureOptions<EmailOptions>
{
private readonly IEmailConfiguration _account;
public ConfigureMyOptions(IEmailConfiguration account)
{
_account = account;
}
public void Configure(EmailOptions options)
{
options.Configuration = _account;
...
}
}
and register it as
services.AddTransient<IConfigureOptions<EmailOptions>, ConfigureEmailOptions>();
and your option class should be just
public class EmailOptions
{
public string DefaultFromAddress { get; set; }
public string DefaultFromDisplayName { get; set; }
public IEmailConfiguration Configuration { get; set; }
}
It's saying you need a public parameterless constructor.
Add
public EmailOptions() {
}
Based on the way configuration works in ASP.NET Core, it will always use a parameterless constructor. Although it's not the only reason why, one particularly good reason is that the configuration setup happens before any DI services are registered, meaning it couldn't inject anything if it wanted to.
Long and short, there's no way to satisfy something like IEmailConfiguration in a strongly-typed configuration class. It's frankly a bad idea anyways. Just let your configuration class be a simple entity and inject that into a service or something that handles your email stuff, instead of the other way around.
I am working on a wcf webservice. This service uses a third party webservice which I have added as a service reference.
Now I want to publish some properties of this proxyclient to clients who uses my wcfservice, without defining an own class and doing the mapping.
The auto generated code is done as partial class.
public partial class Person : object,
System.ComponentModel.INotifyPropertyChanged
{
public string FirstName;
public string LastName;
...
}
I tried to override these properties by using the MetadataType-Attribute and adding the [DataMember]-Attribute to properties. But this seams to work only for EF.
[DataContract]
[MetadataType(typeof(PersonMetaData))]
public partial class Person
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
public class PersonMetaData
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
Trying to implement an interface didn't help, the properties are invisible on client.
[DataContract]
public partial class Person : IPerson
{}
public interface IPerson
{
[DataMember]
string FirstName { get; set; }
[DataMember]
string LastName { get; set; }
}
Any idea?
Guido
On my service XmlSerializer failed to serialize the auto-generated class cause of PropertyChanged-event.
If I work with DataContractSerializer and decorate my auto-generated class with [DataContract], I'm not able to decorate the properties by inheritance with [DataMember] because the attibute is not inheritable.
So I extended this partial class by wrapper properties.
[DataContract]
public partial class Person
{
[DataMember]
public string FirstNameWrapper
{
get
{
return this.FirstName;
}
set
{
this.FirstName = value;
}
}
}
On the service side I have an abstract base class like so:
[DataContract]
public abstract class EntityBase : IObjectState, IDatabaseMetaData
{
[NotMapped]
[DataMember]
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
[DataMember] public DateTime InsertDatetime { get; set; }
[DataMember] public int InsertSystemUserId { get; set; }
[DataMember] public DateTime? UpdateDatetime { get; set; }
[DataMember] public int? UpdateSystemUserId { get; set; }
public virtual SystemUser InsertSystemUser { get; set; }
public virtual SystemUser UpdateSystemUser { get; set; }
#endregion
}
Here is an implementing class (data contract):
[DataContract(Namespace = Constants.MyNamespace)]
public class AccountClass : EntityBase
{
[DataMember] public int AccountClassId { get; set; }
[DataMember] public string AccountClassCode { get; set; }
[DataMember] public string AccountClassDesc { get; set; }
}
On the client side I have essentially duplicated contracts. Here is the Client.AccountClass:
public class AccountClass : ObjectBase
{
private int _accountClassId;
private string _accountClassCode;
private string _accountClassDesc;
public int AccountClassId
{
get { return _accountClassId;}
set
{
if (_accountClassId == value) return;
_accountClassId = value;
OnPropertyChanged(() => AccountClassId);
}
}
public string AccountClassCode
{
get { return _accountClassCode; }
set
{
if (_accountClassCode == value) return;
_accountClassCode = value;
OnPropertyChanged(() => AccountClassCode);
}
}
public string AccountClassDesc
{
get { return _accountClassDesc; }
set
{
if (_accountClassDesc == value) return;
_accountClassDesc = value;
OnPropertyChanged(() => AccountClassDesc);
}
}
}
..and here is the parts of ObjectBase that matter:
public abstract class ObjectBase : IObjectState, IDatabaseMetaData
{
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
public DateTime InsertDatetime { get; set; }
public int InsertSystemUserId { get; set; }
public DateTime? UpdateDatetime { get; set; }
public int? UpdateSystemUserId { get; set; }
#endregion
}
When I debug the service in my WcfMessageInspector.BeforeSendReply, I can see the message correctly sending the IObjectState and IDatabaseMetaData values. However, on the client side, they are always null (or default values). I have tried using KnownTypes, applying the namespace to the abstract class. The only way I can serialize everything correctly is to get rid of the interfaces and base classes all together and put the properties directly on the Client/Server AccountClass object. What am I missing here? Thanks.
Update 1
This seems to be a namespace thing. If I move my EntityBase and ObjectBase into the same CLR Namespace, everything works (with no KnownType attributes). In my client contract's AssemblyInfo.cs file I have this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Project.Name.Client.Entities")]
I tried adding ContractNamespaces here to no avail. Like I said, unless the EntityBase and ObjectBase are in the same namespace, it won't work. However, this is a problem for me because it creates a circular reference, unless I move a lot of stuff around.
Any idea how I can see what the full data contract (namespaces, DataMembers, etc) looks like just before/after serialization on the client/server? I tried intercepting the OnSerializing event without much luck. Thanks again.
This was a namespace issue.
I explicitly add the correct namespace to all parties involved and everything works great. One thing I notice is that the ContractNamespace's ClrNamespace in your AssemblyInfo.cs file should match the AssemblyTitle. Also, putting more than one ContractNamespace in the AssemblyInfo.cs does nothing. For example, I was doing this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities")]
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities.Core")]
Any POCO in the Company.Project.Client.Entities.Core would not serialize correctly until I explicitly put the DataContract namespace on it like so
[DataContract(Namespace = Constants.MyNamespace)]
public class SomeObject
{
[DataMember] public string SomeProperty { get; set; }
//..etc
}
Alternatively, I could have restructured the project so SomeObject was in the Company.Project.Client.Entities namespace and that would have worked.
Finally, the most helpful thing to debugging this was looking at the WSDL, and then using a custom IDispatchMessageInspector to see the actual messages AfterReceiveRequest and BeforeSendReply. Hopefully this helps someone.
Im trying to use a Class in a WCF service. When im calling the
u.attributeChanges.Add(a);
i get:
"Object reference not set to an instance of an object"
If create the classes in the client application it's working.
UpdateChanges Class
[DataContract]
public class UpdateChanges
{
private void Initialize()
{
this.attributeChanges = new List<AttributeChanges>();
}
public UpdateChanges()
{
this.Initialize();
}
[DataMember]
public string objectGuid { get; set; }
[DataMember]
public Utilities.ObjectTypes objectType { get; set; }
[DataMember]
public Utilities.ChangeType changeType{ get; set; }
[DataMember]
public List<AttributeChanges> attributeChanges { get; set; }
[OnDeserializing]
public void OnDeserializing(StreamingContext ctx)
{
this.Initialize();
}
}
AttributeChanges class
[DataContract]
public class AttributeChanges
{
[DataMember]
public string attributeName { get; set; }
[DataMember]
public string attributeValue { get; set; }
}
Client Code:
Service.DirsyncServiceClient proxyClient;
proxyClient = Utilities.GetProxy("http://192.168.1.45/vDir/Service.svc");
Service.UpdateChanges u = new Service.UpdateChanges();
Service.AttributeChanges a = new Service.AttributeChanges();
a.attributeName = "Attribute1";
a.attributeValue = "Value1";
u.attributeChanges.Add(a);
proxyClient.SaveObject(u);
Anyonw know how to solve this?
You're using a generated client code.
The problem is that the client generates this code on base of the WSDL xlm. The code in the CTOR doesn't generated in the client because the client can't be aware of this code.
You have a few options-
1. Use a shared DLL with the data contract instead of generating it via a web reference.
2. Implement it yourself in a 'partial' class.