I am creating a post endpoint using Azure Functions version 3. In Asp.net it is very convenient to get the post object using the [FromBody] tag and the magic will happen with modelbinding.
Is there a way to use the FromBody tag in Azure Functions v3?
Yes you can do that,
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post")][FromBody] User user, ILogger log, ExecutionContext context)
Here is an Example
Microsoft.Azure.Functions.Worker version 1.7.0-preview1 makes custom input conversion possible. The below will convert HttpRequestData.Body to a POCO via converting the stream to a byte array then passing the byte array back into the normal input converter process (where it is convertered by the built-in JsonPocoConverter. It relies on reflection as the services required to delegate the conversion after converting the stream to a byte array are internal, so it may break at some point.
Converter:
internal class FromHttpRequestDataBodyConverter : IInputConverter
{
public async ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
{
if (context.Source is null
|| context.Source is not HttpRequestData req
|| context.TargetType.IsAssignableFrom(typeof(HttpRequestData)))
{
return ConversionResult.Unhandled();
}
var newContext = new MyConverterContext(
context,
await ReadStream(req.Body));
return await ConvertAsync(newContext);
}
private static async Task<ReadOnlyMemory<byte>> ReadStream(Stream source)
{
var byteArray = new byte[source.Length];
using (var memStream = new MemoryStream(byteArray))
{
await source.CopyToAsync(memStream);
}
return byteArray.AsMemory();
}
private static ValueTask<ConversionResult> ConvertAsync(MyConverterContext context)
{
// find the IInputConversionFeature service
var feature = context.FunctionContext
.Features
.First(f => f.Key == InputConvertionFeatureType)
.Value;
// run the default conversion
return (ValueTask<ConversionResult>)(ConvertAsyncMethodInfo.Invoke(feature, new[] { context })!);
}
#region Reflection Helpers
private static Assembly? _afWorkerCoreAssembly = null;
private static Assembly AFWorkerCoreAssembly => _afWorkerCoreAssembly
??= AssemblyLoadContext.Default
.LoadFromAssemblyName(
Assembly.GetExecutingAssembly()
.GetReferencedAssemblies()
.Single(an => an.Name == "Microsoft.Azure.Functions.Worker.Core"))
?? throw new InvalidOperationException();
private static Type? _inputConversionFeatureType = null;
private static Type InputConvertionFeatureType => _inputConversionFeatureType
??= AFWorkerCoreAssembly
.GetType("Microsoft.Azure.Functions.Worker.Context.Features.IInputConversionFeature", true)
?? throw new InvalidOperationException();
private static MethodInfo? _convertAsyncMethodInfo = null;
private static MethodInfo ConvertAsyncMethodInfo => _convertAsyncMethodInfo
??= InputConvertionFeatureType.GetMethod("ConvertAsync")
?? throw new InvalidOperationException();
#endregion
}
Concrete ConverterContext class:
internal sealed class MyConverterContext : ConverterContext
{
public MyConverterContext(Type targetType, object? source, FunctionContext context, IReadOnlyDictionary<string, object> properties)
{
TargetType = targetType ?? throw new ArgumentNullException(nameof(context));
Source = source;
FunctionContext = context ?? throw new ArgumentNullException(nameof(context));
Properties = properties ?? throw new ArgumentNullException(nameof(properties));
}
public MyConverterContext(ConverterContext context, object? source = null)
{
TargetType = context.TargetType;
Source = source ?? context.Source;
FunctionContext = context.FunctionContext;
Properties = context.Properties;
}
public override Type TargetType { get; }
public override object? Source { get; }
public override FunctionContext FunctionContext { get; }
public override IReadOnlyDictionary<string, object> Properties { get; }
}
Service configuration:
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.Configure<WorkerOptions>((workerOptions) =>
{
workerOptions.InputConverters.Register<Converters.FromHttpRequestDataBodyConverter>();
});
})
.Build();
host.Run();
}
}
Related
Can I use AutoMapper 8.0.1 with Blazor server app, please?
I have try it but my code always run into an error:
Missing type map configuration or unsupported mapping. Mapping types:
Object -> Object System.Object -> System.Object
I have added the mapper to the Startup file:
services.AddAutoMapper(typeof(Startup));
I have created the profile:
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<District, DistrictModel>();
}
}
And I try to use it:
[Inject]
protected IMapper Mapper { get; set; }
District district = DistrictService.FindDistrictById(districtId);
DistrictModel model = Mapper.Map<DistrictModel>(district);
The AssertConfigurationIsValid method gives:
Cannot find any profiles with the name 'MyProfile'. (Parameter 'profileName')
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these to interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
your model or viewmodel :
public class District : IMapFrom<District>
{
public string PhoneNumber { get; set; }
public string Password { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<District, DistrictModel>();
}
}
Startup.cs
var mapperConfiguration = new MapperConfiguration(configuration =>
{
configuration.AddProfile(new MyProfile());
});
var mapper = mapperConfiguration.CreateMapper();
services.AddSingleton(mapper);
I have the following code to register Mapping (version 4.2)
public class ModelMapperProfile : Profile
{
protected override void Configure()
{
CreateMap<Case, CaseModel>();
CreateMap<CaseDetail, CaseDetailModel>();
}
}
public static class AutoMapperService
{
public static MapperConfiguration Initialize()
{
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ModelMapperProfile>();
});
return config;
}
}
And I register the dependency using unity as follows...
public static void RegisterTypes(IUnityContainer container)
{
container.LoadConfiguration();
var mapper = AutoMapperService.Initialize()
.CreateMapper();
container.RegisterInstance<IMapper>(mapper);
}
My here service constructor..
public TaxLiabilityCaseService(IMapper mapper,
IUnitOfWork unitofWork,
IRepository<Case> caseR,
IRepository<CaseDetail> caseDetailR)
{
_mapper = mapper;
_unitofWork = unitofWork;
_caseR = caseR;
_caseDetailR = caseDetailR;
}
And I get the following error message..
The current type, AutoMapper.IMapper, is an interface and cannot be
constructed. Are you missing a type mapping?
Answers found here did not work for me
What am I missing here
Try following these steps (MVC5):
Get Unity Nuget package:
Unity.Mvc5
Create this class:
public class MapperConfig
{
public static IMapper Mapper { get; set; }
public static void RegisterProfiles()
{
var config = new MapperConfiguration(cfg =>
{
// add profiles here
});
config.AssertConfigurationIsValid();
Mapper = config.CreateMapper();
}
}
In the UnityConfig file (created by the package), add this:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterInstance<IMapper>(MapperConfig.Mapper);
}
In the Global.asax, add these:
protected void Application_Start()
{
MapperConfig.RegisterProfiles();
UnityConfig.RegisterComponents();
}
You should be good after this.
I have this base class structure:
Base:
public abstract class BackgroundTask
{
protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
protected virtual void Initialize()
{
// initialize database access
}
public void Run()
{
Initialize();
try
{
Execute();
// insert to database or whatever
}
catch (Exception ex)
{
Logger.ErrorException(string.Format("Error proccesing task: {0}\r\n", ToString()), ex);
Exceptions.Add(ex);
}
finally
{
TaskExecuter.Discard();
}
}
protected abstract void Execute();
public abstract override string ToString();
public IList<Exception> Exceptions = new List<Exception>();
}
Task executor:
public static class TaskExecuter
{
private static readonly ThreadLocal<IList<BackgroundTask>> TasksToExecute
= new ThreadLocal<IList<BackgroundTask>>(() => new List<BackgroundTask>());
public static void ExecuteLater(BackgroundTask task)
{
TasksToExecute.Value.Add(task);
}
public static void StartExecuting()
{
foreach (var backgroundTask in TasksToExecute.Value)
{
Task.Factory.StartNew(backgroundTask.Run);
}
}
public static void Discard()
{
TasksToExecute.Value.Clear();
TasksToExecute.Dispose();
}
}
FileTask:
public class FileTask : BackgroundTask
{
protected static string BaseFolder = #"C:\ASCII\";
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private readonly string _folder;
private IHistoryRepository _historyRepository;
public string Folder
{
get { return _folder; }
}
public FileTask(string folder)
{
_folder = string.Format("{0}{1}", BaseFolder, folder);
}
protected override void Initialize()
{
_historyRepository = new HistoryRepository();
}
protected override void Execute()
{
// todo: Get institute that are active,
var institute = MockInstitute(); // todo: uncomment _historyRepository.FindInstituteByFolderName(Folder);
// todo: Update institute, lastupdate - [date] | [files amount] | [phonenumbers amount]
if (institute == null)
{
Logger.Warn("Not found data", Folder);
return;
}
// todo: read file get encoding | type and parse it
Task.Factory.StartNew(ReadFile);
}
private void ReadFile()
{
var list = GetFilesByFolder();
StreamReader sr = null;
try
{
Lock.EnterReadLock();
foreach (var fi in list)
{
var fileName = fi.FullName;
Logger.Info("Line: {0}:=> Content: {1}", fileName, Thread.CurrentThread.ManagedThreadId);
sr = new StreamReader(fileName, DetectEncoding(fileName));
string currentLine;
while ((currentLine = sr.ReadLine()).ReturnSuccess())
{
if (string.IsNullOrEmpty(currentLine)) continue;
Logger.Info("Line: {0}:=> Content: {1}", fileName, currentLine);
}
}
Lock.ExitReadLock();
}
finally
{
if (sr != null) sr.Dispose();
Logger.Info("Finished working" + Folder);
}
}
protected IEnumerable<FileInfo> GetFilesByFolder()
{
return Directory.GetFiles(Folder).Select(fileName => new FileInfo(fileName));
}
protected Encoding DetectEncoding(string file)
{
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var cdet = new Ude.CharsetDetector();
cdet.Feed(fs);
cdet.DataEnd();
return cdet.With(x => x.Charset)
.Return(x => Encoding.GetEncoding(cdet.Charset),
Encoding.GetEncoding("windows-1255"));
}
}
private Institute MockInstitute()
{
return new Institute
{
FromFolderLocation = string.Format("{0}{1}", BaseFolder, Folder)
};
}
public override string ToString()
{
return string.Format("Folder: {0}", Folder);
}
}
When don't read the file every thing ok, the Log is populated and every thing runs smooth,
but when i attach the Task.Factory.StartNew(ReadFile); method i have an exception.
Exception:
Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'.
How do i solve that issue? might i need to change the LocalThread logic, or what - i have been trying to handle that issue, for almost a day.
BTW: It's an MVC4 project, and C# 5.0 and i'm trying to TDD it all.
You shouldn't be calling TasksToExecute.Dispose();
there.
I'm trying to find a clean way to accomplish MvvmLight's IDataService pattern with async/await.
Originally I was using the callback Action method to work similar to the template's but that doesn't update the UI either.
public interface IDataService
{
void GetData(Action<DataItem, Exception> callback);
void GetLocationAsync(Action<Geoposition, Exception> callback);
}
public class DataService : IDataService
{
public void GetData(Action<DataItem, Exception> callback)
{
// Use this to connect to the actual data service
var item = new DataItem("Location App");
callback(item, null);
}
public async void GetLocationAsync(Action<Geoposition, Exception> callback)
{
Windows.Devices.Geolocation.Geolocator locator = new Windows.Devices.Geolocation.Geolocator();
var location = await locator.GetGeopositionAsync();
callback(location, null);
}
}
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _locationString = string.Empty;
public string LocationString
{
get
{
return _locationString;
}
set
{
if (_locationString == value)
{
return;
}
_locationString = value;
RaisePropertyChanged(LocationString);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetLocation(
(location, error) =>
{
LocationString = string.Format("({0}, {1})",
location.Coordinate.Latitude,
location.Coordinate.Longitude);
});
}
}
I'm trying to databind against gps coordinates but since the async fires and doesn't run on main thread it doesn't update the UI.
Might be unrelated, but AFAICT you're missing some quotes
RaisePropertyChanged(LocationString);
You pass the name of the property that changed, not its value.
I want to inject a type-information to the log-call, like
public sealed class Logger
{
private readonly log4net.ILog _logger;
public Logger()
{
// somehow assing this._logger ... doesn't matter actually
}
public void Info<T>(string message)
{
if (!this._logger.IsInfoEnabled)
{
return;
}
var typeOfT = typeof (T);
var typeName = typeOfT.FullName;
// how to set a property here, only for this one single call
// which i can reference in the config
this._logger.Info(message);
}
}
The comment makes it clear: at some point I'd like to inject the FullName of my generic-parameter only for this one specific call.
I am using log4net 1.2.11.0
Actually it's pretty simple:
public class Foo : log4net.Core.LogImpl
{
private static readonly Type DeclaringType = typeof (Foo);
public Foo(log4net.Core.ILoggerWrapper loggerWrapper)
: this(loggerWrapper.Logger) {}
public Foo(log4net.Core.ILogger logger)
: base(logger) {}
protected LoggingEvent GetLoggingEvent<T>(Level level, string message, Exception exception = null)
{
var loggingEvent = new LoggingEvent(DeclaringType, this.Logger.Repository, this.Logger.Name, level, message, exception);
loggingEvent.Properties["type"] = typeof (T).FullName;
return loggingEvent;
}
public void Info<T>(string message)
{
if (!this.IsInfoEnabled)
{
return;
}
var loggingEvent = this.GetLoggingEvent<T>(Level.Info, message);
this.Logger.Log(loggingEvent);
}
public void Info<T>(string message, params object[] args)
{
if (!this.IsInfoEnabled)
{
return;
}
message = string.Format(message, args);
var loggingEvent = this.GetLoggingEvent<T>(Level.Info, message);
this.Logger.Log(loggingEvent);
}
}