OData property with internal setter -- like UpdateDate - asp.net-web-api2

I have a model with a class called "Animal".
The "Animal" class has several properties but let's focus on the following properties:
CreateDate
CreateUser
In the "Animal" class I can get the CreateDate to work by doing the following:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateDate { get; set; }
This lets me generate the CreateDate in the database by setting the default value in the database as "GetDate()".
When an outside caller tries to "set" the CreateDate field on the OData service, it ignores the data being passed.
This makes it a "read only" property for outside callers.
I need to do something similar to the CreateUser except I need to set the CreateUser = System.Threading.Thread.CurrentPrincipal.Identity.Name on the OData server.
If I try a private set then the OData service does not expose the property at all.
If I try a public set then the outside caller can change the property.
In the "Animal" constructor I have set the internal _CreateUser = System.Threading.Thread.CurrentPrincipal.Identity.Name
I'm not sure how to set it on the server side.

Here is what I got to work.
In the model I have the following:
public string CreateUser { get; set; }
[NotMapped]
public string CreateUserName
{
get
{
return CreateUser;
}
set
{
CreateUser = CreateUser ?? System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
}
And then in the WebApiConfig I had the following:
builder.EntitySet<Animal>("Animals"));
builder.EntityType<Animal>().Ignore(p => p.CreateUser); // hide CreateUser
builder.StructuralTypes.First(t => t.ClrType == typeof(Animal))
.AddProperty(typeof(Animal).GetProperty(nameof(Animal.CreateUserName))); // show CreateUserName

You could fake it out with an empty setter. It will see the public set and generate the property in the OData entity. Not the cleanest solution but it should work.
public class Animal
{
private string _createUser;
public string CreateUser
{
get { return _createUser; }
set { }
}
internal SetCreateUser(string user)
{
_createUser = user;
}
}

Related

How to make an IOptions section optional in .NET Core?

Consider an example service that optionally supports LDAP authentication, otherwise, it does something like local Identity authentication. When LDAP is completely configured, appsettings.json might look like this...
{
"LdapOptions": {
"Host": "ldap.example.com",
"Port": 389
}
}
With an options class.
public class LdapOptions
{
public string Host { get; set; }
public int Port { get; set; } = 389;
}
And Startup has the expected Configure call.
service.Configure<LdapOptions>(nameof(LdapOptions));
This work great when I have a complete valid "LdapOptions" section. But, it's not so great if I intentionally leave the section out of my appsettings.
An IOptions<TOptions> instance resolves even if I leave the section out of my appsettings entirely; it even resolves if I remove the Startup configure call entirely! I get an object that appears, based on property values, to be default(TOptions).
public AuthenticationService(IOptions<LdapOptions> ldapOptions)
{
this.ldapOptions = ldapOptions.Value; // never null, sometimes default(LdapOptions)!
}
I don't want to depend on checking properties if a section is intentionally left out. I can imagine scenarios where all of the properties in an object have explicit defaults and this wouldn't work. I'd like something like a Maybe<TOptions> with a HasValue property, but I'll take a null.
Is there any way to make an options section optional?
Update: Be aware that I also intend to validate data annotations...
services.AddOptions<LdapOptions>()
.Configure(conf.GetSection(nameof(LdapOptions)))
.ValidateDataAnnotations();
So, what I really want is for optional options to be valid when the section is missing (conf.Exists() == false) and then normal validations to kick in when the section is partially or completely filled out.
I can't imagine any solution working with data annotation validations that depends on the behavior of creating a default instance (for example, there is no correct default for Host, so a default instance will always be invalid).
The whole idea of IOptions<T> is to have non-null default values, so that your settings file doesn't contain hundreds/thousands sections to configure the entire ASP pipeline
So, its not possible to make it optional in the sense that you will get null, but you can always defined some "magic" property to indicate whether this was configured or not:
public class LdapOptions
{
public bool IsEnabled { get; set; } = false;
public string Host { get; set; }
public int Port { get; set; } = 389;
}
and your app settings file:
{
"LdapOptions": {
"IsEnabled: true,
"Host": "ldap.example.com",
"Port": 389
}
}
Now, if you keep 'IsEnabled' consistently 'true' in your settings, if IsEnabled is false, that means the section is missing.
An alternative solution is to use a different design approach, e.g. put the auth type in the settings file:
public class LdapOptions
{
public string AuthType { get; set; } = "Local";
public string Host { get; set; }
public int Port { get; set; } = 389;
}
And your app settings:
{
"LdapOptions": {
"AuthType : "LDAP",
"Host": "ldap.example.com",
"Port": 389
}
}
This is IMO a cleaner & more consistent approach
If you must have a logic that is based on available/missing section, you can also configure it directly:
var section = conf.GetSection(nameof(LdapOptions));
var optionsBuilder = services.AddOptions<LdapOptions>();
if section.Value != null {
optionsBuilder.Configure(section).ValidateDataAnnotations();
}
else {
optionsBuilder.Configure(options => {
// Set defaults here
options.Host = "Deafult Host";
}
}
I wanted to avoid lambdas in Startup that would need to be copy/pasted correctly for every "optional" section and I wanted to be very explicit about optionality (at the expense of some awkward naming).
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddOption<Optional<LdapOptions>>()
.ConfigureOptional(conf.GetSection(nameof(LdapOptions)))
.ValidateOptionalDataAnnotations();
}
The Optional type is pretty straightforward, but may need a better name (to avoid interfering with other implementations of the generic Option/Some/Maybe pattern). I thought about just using null, but that seemed contrary to Options insistence on returning something no matter what.
Optional.cs
public class Optional<TOptions> where TOptions : class
{
public TOptions Value { get; set; }
public bool HasValue { get => !(Value is null); }
}
The configure extension method takes into account section existence.
OptionalExtensions.cs
public static class OptionalExtensions
{
public static OptionsBuilder<Optional<TOptions>> ConfigureOptional<TOptions>(this OptionsBuilder<Optional<TOptions>> optionsBuilder, IConfigurationSection config) where TOptions : class
{
return optionsBuilder.Configure(options =>
{
if (config.Exists())
{
options.Value = config.Get<TOptions>();
}
});
}
public static OptionsBuilder<Optional<TOptions>> ValidateOptionalDataAnnotations<TOptions>(this OptionsBuilder<Optional<TOptions>> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<Optional<TOptions>>>(new DataAnnotationValidateOptional<TOptions>(optionsBuilder.Name));
return optionsBuilder;
}
}
The validate extension method works with a custom options validator that also takes into account how missing sections work (like the comment says, "missing optional options are always valid").
DataAnnotationValidateOptional.cs
public class DataAnnotationValidateOptional<TOptions> : IValidateOptions<Optional<TOptions>> where TOptions : class
{
private readonly DataAnnotationValidateOptions<TOptions> innerValidator;
public DataAnnotationValidateOptional(string name)
{
this.innerValidator = new DataAnnotationValidateOptions<TOptions>(name);
}
public ValidateOptionsResult Validate(string name, Optional<TOptions> options)
{
if (options.Value is null)
{
// Missing optional options are always valid.
return ValidateOptionsResult.Success;
}
return this.innerValidator.Validate(name, options.Value);
}
}
Now, anywhere you need to use an optional option, like, say, a login controller, you can take the following actions...
LdapLoginController.cs
[ApiController]
[Route("/api/login/ldap")]
public class LdapLoginController : ControllerBase
{
private readonly Optional<LdapOptions> ldapOptions;
public LdapLoginController(IOptionsSnapshot<Optional<LdapOptions>> ldapOptions)
{
// data annotations should trigger here and possibly throw an OptionsValidationException
this.ldapOptions = ldapOptions.Value;
}
[HttpPost]
public void Post(...)
{
if (!ldapOptions.Value.HasValue)
{
// a missing section is valid, but indicates that this option was not configured; I figure that relates to a 501 Not Implemented
return StatusCode((int)HttpStatusCode.NotImplemented);
}
// else we can proceed with valid options
}
}

EF Core 3.1 property value returns default field value

I have a class property of enum type LogLevel (using Microsoft.Extensions.Logging) being stored in my database and a class field that ins't mapped but stores the enum type value as such:
class ...
{
private LogLevel LevelName;
[Required]
public int Level { get { return (int)LevelName; } set { LevelName = (LogLevel)Level; } }
}
But since the field has to be initialized as default, every time I retrieve the value from the database I get back the default value of 0 for Level even though its really 3 or something. If the value is never being set, How can I initialize the correct default value?
You can use enums in Entity Framework Core models:
public class YourModel
{
public UserSearchStatus SearchStatus { get; set; }
}
with an enum like this.
public enum UserSearchStatus
{
StatusOne = 1,
StatusTwo = 2
}
If you don't want to use a default value, make the property nullable:
public class YourModel
{
public UserSearchStatus? SearchStatus { get; set; }
}

WEBAPI ActionContext Request.Properties.Add for store sensitive information

I would like to pass information from the action filter (database) to the Action function.
Is it secure to use ActionContext Request.Properties.Add to store the data?
is there any chance that the information will be seen by the WEBAPI client or its safe as it safe to store information in the Cache\Session?
Is it a better way to do it?
The client will not see request properties unless you explicitly serialize them. They completely remain on the server side.
To answer your followup question here are two other ways to do it. There is no "Best" way per se. It all depends on how far you want the information to flow, and how generic you want your filter to be. My personal preference is using the controller object, but again it is just a preference.
For the sample here is a simple values controller and a POCO class:
[MyActionfilter]
public class ValuesController : ApiController
{
public string Foo { get; set; }
public User Get(User user)
{
if (Foo != null && user != null)
{
user.FamilyName = Foo;
}
return user;
}
}
public class User
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
}
The action filter below is naively implementing access to the controller object or the method parameters. Note that it's up to you to either apply the filter sparingly or do type checks/dictionary checks.
public class MyActionfilter : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
controller = actionContext.ControllerContext.Controller;
// Not safe unless applied only to controllers deriving
// from ValuesController
((ValuesController)controller).Foo = "From filter";
// Not safe unless you know the user is on the signature
// of the action method.
actionContext.ActionArguments["user"] = new User()
{
FirstName = "From filter"
};
}
}

How to specify the Execution Order of Custom Validation Attributes in MVC 4?

I have multiple custom validation attributes in property level as well as Class level. I just would would like to know how to set the execution order of ValidationAttributes. Because one of my validation attribute to check "Required Fields" never gets called?
public class PhoneNumberFormatAttribute : ValidationAttribute
{
.......
return new ValidationResult("Invalid Phone Number);
.......
}
public class RequiredFieldsAttribute : ValidationAttribute
{
.........
return new ValidationResult("Field Required");
..........
}
[RequiredFields]
public class MessageRequest
{
[PhoneNumberFormat]
public string PhoneNo { get; set; }
}
If I get model state error from PhoneNumberFormatAttribute, the RequiredFieldAttribute never gets called.

Serialising classes that implement List<T> for transferring over WCF

I have spent some time writing code for my application assuming that the serialisation bit would be the easiest part of it. Pretty much both sides (client and server) are done and all I need to do is passing a class AccountInfo from the service to the client... The problem is that AccountInfo inherits List and therefore [DataContract] attribute is not valid. I tried using the [CollectionDataContract] attribute but then the class that is received on the other side (client) contains only generic List methods without my custom implemented properties such as GroupTitle...I have worked out a solution for this problem but I don't know how to apply it.
Basically everything works when I make a property instead of inheriting a List but then I can't bind this class to LongListSelector (WP7) because it's not a collection type.
There are three classes I'm on about. AccountInfo that contains multiple instances of: AccountInfoGroup that contains multiple instances of:AccountInfoEntry (this one does not inherit list therefore there are no problems serialising it and all properties are accessible).
Could someone help me using right attributes to serialise and transfer these classes using a WCF method?
Here is the code of 2 of these collection classes:
public class AccountInfo : List<AccountInfoGroup>
{
public AccountInfo()
{
UpdateTime = DateTime.UtcNow;
EntryID = Guid.NewGuid();
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
public Guid EntryID
{
get;
set;
}
public decimal GetTotalCredit()
{
decimal credit = 0;
foreach (AccountInfoGroup acg in this.Where(item => item.Class == AccountInfoEntry.EntryType.Credit))
{
acg.Where(item => item.ItemClass == AccountInfoEntry.EntryType.Credit).ToList().ForEach(entry =>
{ credit += entry.Remaining; }
);
}
return credit;
}
public bool UsedForCreditComparison = false;
public DateTime UpdateTime { get; private set; }
}
public class AccountInfoGroup : List<AccountInfoEntry>
{
public AccountInfoEntry.EntryType Class
{
get;
private set;
}
public string Title
{
get
{
return AccountInfoEntry.ClassToString(Class);
}
}
public AccountInfoGroup(AccountInfoEntry.EntryType groupClass)
{
this.#Class = groupClass;
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
}
Thank you for any suggestions... :)
The sample you had is quite painful for WCF in serialization.
What I suggest is you to revised and have a common models for your WCF messages (That means it only contains properties with getter and setter, serialization attributes).
If you have a problem in LongListSelector binding in WP7, you might want to convert the message to the actual type the WP7 object supports to use in binding.