I have a class like so:
public class CompanyData
{
# region Properties
/// <summary>
/// string CompanyNumber
/// </summary>
private string strCompanyNumber;
/// <summary>
/// string CompanyName
/// </summary>
private string strCompanyName;
[Info("companynumber")]
public string CompanyNumber
{
get
{
return this.strCompanyNumber;
}
set
{
this.strCompanyNumber = value;
}
}
/// <summary>
/// Gets or sets CompanyName
/// </summary>
[Info("companyName")]
public string CompanyName
{
get
{
return this.strCompanyName;
}
set
{
this.strCompanyName = value;
}
}
/// <summary>
/// Initializes a new instance of the CompanyData class
/// </summary>
public CompanyData()
{
}
/// <summary>
/// Initializes a new instance of the CompanyData class
/// </summary>
/// <param name="other"> object company data</param>
public CompanyData(CompanyData other)
{
this.Init(other);
}
/// <summary>
/// sets the Company data attributes
/// </summary>
/// <param name="other">object company data</param>
protected void Init(CompanyData other)
{
this.CompanyNumber = other.CompanyNumber;
this.CompanyName = other.CompanyName;
}
/// <summary>
/// Getting array of entity properties
/// </summary>
/// <returns>An array of PropertyInformation</returns>
public PropertyInfo[] GetEntityProperties()
{
PropertyInfo[] thisPropertyInfo;
thisPropertyInfo = this.GetType().GetProperties();
return thisPropertyInfo;
}
}
A csv file is read and a collection of CompanyData objects is created
In this method I am trying to get properties and values:
private void GetPropertiesAndValues(List<CompanyData> resList)
{
foreach (CompanyData resRow in resList)
{
this.getProperties = resRow.ExternalSyncEntity.GetEntityProperties();
this.getValues = resRow.ExternalSyncEntity.GetEntityValue(resRow.ExternalSyncEntity);
}
}
Here's the problem, for the first object the GetEntityProperties() returns the CompanyNumber as the first element in the array. For the remaining objects it returns CompanyName as the first element.
Why is the sequence not consistent?
Regards.
The Type.GetProperties() does not return ordered result.
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies.
If you want to use ordered/consistent result, it is better to sort the returned array.
Related
I can find quite many resources on how to generate C# models from protobuf messages (it's even built into the Grpc.AspNetCore package), but not the other way around.
I've created a blazor webassembly client/server project similar to this one: https://github.com/stevejgordon/gRPCBasicSample/
I have an Domain layer with lots of C# models that I'm using for the Application. I would like to convert these models into message reply "view models", instead of writing them all in hand (also I'm not even sure of the right conversion between c# and protobuf)
Example:
C# Model:
/// <summary>
/// The area.
/// </summary>
public class Area
{
/// <summary>
/// Initializes a new instance of the <see cref="Area"/> class.
/// </summary>
/// <param name="name">Name of the region.</param>
/// <param name="areaType">What type of area is the region.</param>
/// <param name="numberOfSupplies">number of supplies the region has.</param>
/// <param name="numberOfMusterCrows">number of crowns the region has.</param>
public Area(string name, AreaType areaType, int numberOfSupplies, int numberOfMusterCrows)
{
this.Name = name;
this.AreaType = areaType;
this.NumberOfSupplies = numberOfSupplies;
this.NumberOfMusterCrowns = numberOfMusterCrows;
this.CurrentArmy = new Collection<Unit>();
this.AdjacentAreas = new Collection<Area>();
}
/// <summary>
/// Gets the name of the region.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets or sets which house the area is controlled by.
/// </summary>
public House ControlledBy { get; set; }
/// <summary>
/// Gets the type of area the region is.
/// </summary>
public AreaType AreaType { get; }
/// <summary>
/// Gets the adjacent regions the region has.
/// </summary>
public IEnumerable<Area> AdjacentAreas { get; internal set; }
/// <summary>
/// Gets the number of supplies (barrels) the region has.
/// </summary>
public int NumberOfSupplies { get; }
/// <summary>
/// Gets the number of crowns the region has.
/// </summary>
public int NumberOfMusterCrowns { get; }
/// <summary>
/// Gets or sets a value indicating whether the region is occupied by troops.
/// </summary>
public bool IsOccupied { get; set; }
/// <summary>
/// Gets a value indicating whether the region has a stronghold.
/// </summary>
public bool HasStronghold { get; }
/// <summary>
/// Gets a value indicating whether the region has a castle.
/// </summary>
public bool HasCastle { get; }
/// <summary>
/// Gets or sets the current armies occupying the region.
/// </summary>
public ICollection<Unit> CurrentArmy { get; set; }
/// <summary>
/// Gets or sets the current order token placed.
/// </summary>
public OrderToken? PlacedOrderToken { get; set; }
}
Should turn into something like this (simplified):
message AreaReply {
string name = 1;
enum AreaType {
Land = 0;
Water = 1;
}
AreaType areaType = 4;
House controlledBy = 5;
}
message House {
google.protobuf.StringValue name = 1;
}
Is there really no such generator out there? :)
Thanks
Use this annotation on top of your class definition:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
See: Protobuf-net serialization without annotation
I use plugin Cash On Delivery in Nopcommerce. But I want to edit the code, that hide Cash on Delivery payment when customer choose Pick-up in store. In the code, I find HidePaymentMethod, but I dont know how return true, when customer chooser radio button pick-up in store.
I am using nopcommerce 4.2.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Plugin.Payments.CashOnDelivery.Controllers;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Plugins;
namespace Nop.Plugin.Payments.CashOnDelivery
{
/// <summary>
/// CashOnDelivery payment processor
/// </summary>
public class CashOnDeliveryPaymentProcessor : BasePlugin, IPaymentMethod
{
#region Fields
private readonly CashOnDeliveryPaymentSettings _cashOnDeliveryPaymentSettings;
private readonly ILocalizationService _localizationService;
private readonly IPaymentService _paymentService;
private readonly ISettingService _settingService;
private readonly IShoppingCartService _shoppingCartService;
private readonly IWebHelper _webHelper;
#endregion
#region Ctor
public CashOnDeliveryPaymentProcessor(CashOnDeliveryPaymentSettings cashOnDeliveryPaymentSettings,
ILocalizationService localizationService,
IPaymentService paymentService,
ISettingService settingService,
IShoppingCartService shoppingCartService,
IWebHelper webHelper)
{
_cashOnDeliveryPaymentSettings = cashOnDeliveryPaymentSettings;
_localizationService = localizationService;
_paymentService = paymentService;
_settingService = settingService;
_shoppingCartService = shoppingCartService;
_webHelper = webHelper;
}
#endregion
#region Methods
/// <summary>
/// Process a payment
/// </summary>
/// <param name="processPaymentRequest">Payment info required for an order processing</param>
/// <returns>Process payment result</returns>
public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest)
{
var result = new ProcessPaymentResult { NewPaymentStatus = PaymentStatus.Pending };
return result;
}
/// <summary>
/// Post process payment (used by payment gateways that require redirecting to a third-party URL)
/// </summary>
/// <param name="postProcessPaymentRequest">Payment info required for an order processing</param>
public void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest)
{
//nothing
}
/// <summary>
/// Returns a value indicating whether payment method should be hidden during checkout
/// </summary>
/// <param name="cart">Shoping cart</param>
/// <returns>true - hide; false - display.</returns>
public bool HidePaymentMethod(IList<ShoppingCartItem> cart)
{
//you can put any logic here
//for example, hide this payment method if all products in the cart are downloadable
//or hide this payment method if current customer is from certain country
return _cashOnDeliveryPaymentSettings.ShippableProductRequired && !_shoppingCartService.ShoppingCartRequiresShipping(cart);
}
/// <summary>
/// Gets additional handling fee
/// </summary>
/// <param name="cart">Shoping cart</param>
/// <returns>Additional handling fee</returns>
public decimal GetAdditionalHandlingFee(IList<ShoppingCartItem> cart)
{
var result = _paymentService.CalculateAdditionalFee(cart,
_cashOnDeliveryPaymentSettings.AdditionalFee, _cashOnDeliveryPaymentSettings.AdditionalFeePercentage);
return result;
}
/// <summary>
/// Captures payment
/// </summary>
/// <param name="capturePaymentRequest">Capture payment request</param>
/// <returns>Capture payment result</returns>
public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
{
var result = new CapturePaymentResult();
result.AddError("Capture method not supported");
return result;
}
/// <summary>
/// Refunds a payment
/// </summary>
/// <param name="refundPaymentRequest">Request</param>
/// <returns>Result</returns>
public RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest)
{
var result = new RefundPaymentResult();
result.AddError("Refund method not supported");
return result;
}
/// <summary>
/// Voids a payment
/// </summary>
/// <param name="voidPaymentRequest">Request</param>
/// <returns>Result</returns>
public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
{
var result = new VoidPaymentResult();
result.AddError("Void method not supported");
return result;
}
/// <summary>
/// Process recurring payment
/// </summary>
/// <param name="processPaymentRequest">Payment info required for an order processing</param>
/// <returns>Process payment result</returns>
public ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest)
{
var result = new ProcessPaymentResult();
result.AddError("Recurring payment not supported");
return result;
}
/// <summary>
/// Cancels a recurring payment
/// </summary>
/// <param name="cancelPaymentRequest">Request</param>
/// <returns>Result</returns>
public CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest)
{
var result = new CancelRecurringPaymentResult();
result.AddError("Recurring payment not supported");
return result;
}
/// <summary>
/// Gets a value indicating whether customers can complete a payment after order is placed but not completed (for redirection payment methods)
/// </summary>
/// <param name="order">Order</param>
/// <returns>Result</returns>
public bool CanRePostProcessPayment(Order order)
{
if (order == null)
throw new ArgumentNullException(nameof(order));
//it's not a redirection payment method. So we always return false
return false;
}
public IList<string> ValidatePaymentForm(IFormCollection form)
{
var warnings = new List<string>();
return warnings;
}
public ProcessPaymentRequest GetPaymentInfo(IFormCollection form)
{
var paymentInfo = new ProcessPaymentRequest();
return paymentInfo;
}
public override string GetConfigurationPageUrl()
{
return $"{_webHelper.GetStoreLocation()}Admin/PaymentCashOnDelivery/Configure";
}
public Type GetControllerType()
{
return typeof(PaymentCashOnDeliveryController);
}
public override void Install()
{
var settings = new CashOnDeliveryPaymentSettings
{
DescriptionText = "<p>In cases where an order is placed, an authorized representative will contact you, personally or over telephone, to confirm the order.<br />After the order is confirmed, it will be processed.<br />Orders once confirmed, cannot be cancelled.</p><p>P.S. You can edit this text from admin panel.</p>",
SkipPaymentInfo = false
};
_settingService.SaveSetting(settings);
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.DescriptionText", "Description");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.DescriptionText.Hint", "Enter info that will be shown to customers during checkout");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFee", "Additional fee");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFee.Hint", "The additional fee.");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFeePercentage", "Additional fee. Use percentage");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFeePercentage.Hint", "Determines whether to apply a percentage additional fee to the order total. If not enabled, a fixed value is used.");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.ShippableProductRequired", "Shippable product required");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.ShippableProductRequired.Hint", "An option indicating whether shippable products are required in order to display this payment method during checkout.");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.PaymentMethodDescription", "Pay by \"Cash on delivery\"");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.SkipPaymentInfo", "Skip payment information page");
_localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payment.CashOnDelivery.SkipPaymentInfo.Hint", "An option indicating whether we should display a payment information page for this plugin.");
base.Install();
}
public override void Uninstall()
{
//settings
_settingService.DeleteSetting<CashOnDeliveryPaymentSettings>();
//locales
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.DescriptionText");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.DescriptionText.Hint");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFee");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFee.Hint");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFeePercentage");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.AdditionalFeePercentage.Hint");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.ShippableProductRequired");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.ShippableProductRequired.Hint");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.PaymentMethodDescription");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.SkipPaymentInfo");
_localizationService.DeletePluginLocaleResource("Plugins.Payment.CashOnDelivery.SkipPaymentInfo.Hint");
base.Uninstall();
}
/// <summary>
/// Gets a name of a view component for displaying plugin in public store ("payment info" checkout step)
/// </summary>
/// <returns>View component name</returns>
public string GetPublicViewComponentName()
{
return "PaymentCashOnDelivery";
}
#endregion
#region Properies
/// <summary>
/// Gets a value indicating whether capture is supported
/// </summary>
public bool SupportCapture => false;
/// <summary>
/// Gets a value indicating whether partial refund is supported
/// </summary>
public bool SupportPartiallyRefund => false;
/// <summary>
/// Gets a value indicating whether refund is supported
/// </summary>
public bool SupportRefund => false;
/// <summary>
/// Gets a value indicating whether void is supported
/// </summary>
public bool SupportVoid => false;
/// <summary>
/// Gets a recurring payment type of payment method
/// </summary>
public RecurringPaymentType RecurringPaymentType => RecurringPaymentType.NotSupported;
/// <summary>
/// Gets a payment method type
/// </summary>
public PaymentMethodType PaymentMethodType => PaymentMethodType.Standard;
/// <summary>
/// Gets a value indicating whether we should display a payment information page for this plugin
/// </summary>
public bool SkipPaymentInfo => _cashOnDeliveryPaymentSettings.SkipPaymentInfo;
/// <summary>
/// Gets a payment method description that will be displayed on checkout pages in the public store
/// </summary>
public string PaymentMethodDescription
{
get
{
return _localizationService.GetResource("Plugins.Payment.CashOnDelivery.PaymentMethodDescription");
}
}
#endregion
}
}
The shipping method is saved as a GenericAttribute of the current customer. So, you can get via IGenericAttributeService (if you don't inject it in your PaymentProcessor class, you have to inject it). If we assume that the system name of your intended shipping method is "Nop.Shipping.PickupInStore", then we can hide the payment method like this:
public bool HidePaymentMethod(IList<ShoppingCartItem> cart)
{
var customer = cart.FirstOrDefault(item => item.Customer != null)?.Customer;
ShippingOption shippingOption = _genericAttributeService.GetAttribute<ShippingOption>(customer, NopCustomerDefaults.SelectedShippingOptionAttribute, _storeContext.CurrentStore.Id);
var shippingMethodSystemName = "Nop.Shipping.PickupInStore";
if (shippingOption!= null && shippingOption.ShippingRateComputationMethodSystemName.Equals(shippingMethodSystemName))
return true;
return false;
}
i'm trying to bind a model with a IFormFile or IFormFileCollection property to my custom class CommonFile. i have not found so much documentation on internet about it using asp .net core, i tried to follow this link Custom Model Binding in ASP.Net Core 1.0
but it is binding a SimpleType property and i need to bind a complex type. Anyway i tried to make my version of this binding and i've got the following code:
FormFileModelBinderProvider.cs
public class FormFileModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (!context.Metadata.IsComplexType) return null;
var isIEnumerableFormFiles = context.Metadata.ModelType.GetInterfaces().Contains(typeof(IEnumerable<CommonFile>));
var isFormFile = context.Metadata.ModelType.IsAssignableFrom(typeof(CommonFile));
if (!isFormFile && !isIEnumerableFormFiles) return null;
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property,
context.CreateBinder);
return new FormFileModelBinder(propertyBinders);
}
}
FromFileModelBinder.cs
the following code is incomplete because i'm not getting any result with bindingContext.ValueProvider.GetValue(bindingContext.ModelName); while i'm debugging everything is going well until bindingContext.ModelName has no a value and i can't bind my model From httpContext to Strongly typed Models.
public class FormFileModelBinder : IModelBinder
{
private readonly ComplexTypeModelBinder _baseBinder;
public FormFileModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders)
{
_baseBinder = new ComplexTypeModelBinder(propertyBinders);
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
return Task.CompletedTask;
}
}
Any suggestions?
After 10 months i found a solution of that i wanted to do.
In Summary: I Want to replace IFormFile IFormFileCollection with my own classes not attached to Asp .Net because my view models are in different project with poco classes. My custom classes are ICommonFile, ICommonFileCollection, IFormFile (not Asp .net core class) and IFormFileCollection.
i will share it here:
ICommonFile.cs
/// <summary>
/// File with common Parameters including bytes
/// </summary>
public interface ICommonFile
{
/// <summary>
/// Stream File
/// </summary>
Stream File { get; }
/// <summary>
/// Name of the file
/// </summary>
string Name { get; }
/// <summary>
/// Gets the file name with extension.
/// </summary>
string FileName { get; }
/// <summary>
/// Gets the file length in bytes.
/// </summary>
long Length { get; }
/// <summary>
/// Copies the contents of the uploaded file to the <paramref name="target"/> stream.
/// </summary>
/// <param name="target">The stream to copy the file contents to.</param>
void CopyTo(Stream target);
/// <summary>
/// Asynchronously copies the contents of the uploaded file to the <paramref name="target"/> stream.
/// </summary>
/// <param name="target">The stream to copy the file contents to.</param>
/// <param name="cancellationToken">Enables cooperative cancellation between threads</param>
Task CopyToAsync(Stream target, CancellationToken cancellationToken = default(CancellationToken));
}
ICommonFileCollection.cs
/// <inheritdoc />
/// <summary>
/// Represents the collection of files.
/// </summary>
public interface ICommonFileCollection : IReadOnlyList<ICommonFile>
{
/// <summary>
/// File Indexer by name
/// </summary>
/// <param name="name">File name index</param>
/// <returns>File with related file name index</returns>
ICommonFile this[string name] { get; }
/// <summary>
/// Gets file by name
/// </summary>
/// <param name="name">file name</param>
/// <returns>File with related file name index</returns>
ICommonFile GetFile(string name);
/// <summary>
/// Gets Files by name
/// </summary>
/// <param name="name"></param>>
/// <returns>Files with related file name index</returns>
IReadOnlyList<ICommonFile> GetFiles(string name);
}
IFormFile.cs
/// <inheritdoc />
/// <summary>
/// File transferred by HttpProtocol, this is an independent
/// Asp.net core interface
/// </summary>
public interface IFormFile : ICommonFile
{
/// <summary>
/// Gets the raw Content-Type header of the uploaded file.
/// </summary>
string ContentType { get; }
/// <summary>
/// Gets the raw Content-Disposition header of the uploaded file.
/// </summary>
string ContentDisposition { get; }
}
IFormFileCollection.cs
/// <summary>
/// File Collection transferred by HttpProtocol, this is an independent
/// Asp.net core implementation
/// </summary>
public interface IFormFileCollection
{
//Use it when you need to implement new features to Form File collection over HttpProtocol
}
I finally created my model binders successfully, i will share it too:
FormFileModelBinderProvider.cs
/// <inheritdoc />
/// <summary>
/// Model Binder Provider, it inspects
/// any model when the request is triggered
/// </summary>
public class FormFileModelBinderProvider : IModelBinderProvider
{
/// <inheritdoc />
/// <summary>
/// Inspects a Model for any CommonFile class or Collection with
/// same class if exist the FormFileModelBinder initiates
/// </summary>
/// <param name="context">Model provider context</param>
/// <returns>a new Instance o FormFileModelBinder if type is found otherwise null</returns>
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (!context.Metadata.IsComplexType) return null;
var isSingleCommonFile = IsSingleCommonFile(context.Metadata.ModelType);
var isCommonFileCollection = IsCommonFileCollection(context.Metadata.ModelType);
if (!isSingleCommonFile && !isCommonFileCollection) return null;
return new FormFileModelBinder();
}
/// <summary>
/// Checks if object type is a CommonFile Collection
/// </summary>
/// <param name="modelType">Context Meta data ModelType</param>
/// <returns>If modelType is a collection of CommonFile returns true otherwise false</returns>
private static bool IsCommonFileCollection(Type modelType)
{
if (typeof(ICommonFileCollection).IsAssignableFrom(modelType))
{
return true;
}
var hasCommonFileArguments = modelType.GetGenericArguments()
.AsParallel().Any(t => typeof(ICommonFile).IsAssignableFrom(t));
if (typeof(IEnumerable).IsAssignableFrom(modelType) && hasCommonFileArguments)
{
return true;
}
if (typeof(IAsyncEnumerable<object>).IsAssignableFrom(modelType) && hasCommonFileArguments)
{
return true;
}
return false;
}
/// <summary>
/// Checks if object type is CommonFile or an implementation of ICommonFile
/// </summary>
/// <param name="modelType"></param>
/// <returns></returns>
private static bool IsSingleCommonFile(Type modelType)
{
if (modelType == typeof(ICommonFile) || modelType.GetInterfaces().Contains(typeof(ICommonFile)))
{
return true;
}
return false;
}
}
FormFileModelBinder.cs
/// <inheritdoc />
/// <summary>
/// Form File Model binder
/// Parses the Form file object type to a commonFile
/// </summary>
public class FormFileModelBinder : IModelBinder
{
/// <summary>
/// Expression to map IFormFile object type to CommonFile
/// </summary>
private readonly Func<Microsoft.AspNetCore.Http.IFormFile, ICommonFile> _expression;
/// <summary>
/// FormFile Model binder constructor
/// </summary>
public FormFileModelBinder()
{
_expression = x => new CommonFile(x.OpenReadStream(), x.Length, x.Name, x.FileName);
}
/// <inheritdoc />
/// <summary>
/// It Binds IFormFile to Common file, getting the file
/// from the binding context
/// </summary>
/// <param name="bindingContext">Http Context</param>
/// <returns>Completed Task</returns>
// TODO: Bind this context to ICommonFile or ICommonFileCollection object
public Task BindModelAsync(ModelBindingContext bindingContext)
{
dynamic model;
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var formFiles = bindingContext.ActionContext.HttpContext.Request.Form.Files;
if (!formFiles.Any()) return Task.CompletedTask;
if (formFiles.Count > 1)
{
model = formFiles.AsParallel().Select(_expression);
}
else
{
model = new FormFileCollection();
model.AddRange(filteredFiles.AsParallel().Select(_expression));
}
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Actually Everything is working good except when i have Nested Models. I share an example of my models I'm using and I'll do some comments with working scenarios and which don't
Test.cs
public class Test
{
//It's Working
public ICommonFileCollection Files { get; set; }
//It's Working
public ICommonFileCollection Files2 { get; set; }
//This is a nested model
public TestExtra TestExtra { get; set; }
}
TestExtra.cs
public class TestExtra
{
//It's not working
public ICommonFileCollection Files { get; set; }
}
Actually when i make a request to my API I've got the following (Screenshot):
I'm sharing a screenshot of my postman request too for clarifying my request is good.
If there is any subjection to make this work with nested model it would be great.
UPDATE
Asp Net Core Model Binder won't bind model with one property only, if you have one property in a class, that property will be null but when you add two or more will bind it. my mistake i had one one property in a nested class. The entire code is correct.
Sorry for the general title but it's a bit hard to explain in few words what is my problem currently.
So I have a simple class factory like this:
public Model Construct<T>(T param) where T : IModelable
{
new Model {Resource = param};
return n;
}
The Model class looks like this:
public class Model
{
public object Resource { get; set; }
}
The problem is, that you can see, is the Resource is currently an object. And I would like that Resource should be the type, what is get from the Construct and not lost the type-safe...
I tried to solve it with type parameter but it fails, because I can extend Model class with type parameter but what if I would like to store it to a simple class repository?
Then Construct will work, but if I would like to get the instanced class from the repository, I have to declare the type paramter again like:
Repository.Get<Model<Spaceship>>(0) .... and of course it's wrong because I would like that Model itself knows, what type of Resource has been added in Construct.
Does anybody any idea how to handle this?
The whole code currently look like this:
/// <summary>
/// Class Repository
/// </summary>
public sealed class Repository
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _syncroot
/// </summary>
private static readonly object _syncroot = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile Repository _instance;
/// <summary>
/// The _dict
/// </summary>
private static readonly Dictionary<int, object> _dict
= new Dictionary<int, object>();
/// <summary>
/// Prevents a default data of the <see cref="Repository" /> class from being created.
/// </summary>
private Repository()
{
}
/// <summary>
/// Gets the items.
/// </summary>
/// <value>The items.</value>
public static Repository Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new Repository();
}
}
return _instance;
}
}
/// <summary>
/// Allocates the specified id.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="parameter">The parameter.</param>
/// <resource name="id">The id.</resource>
/// <resource name="parameter">The parameter.</resource>
public void Allocate(int id, object parameter)
{
lock (_syncroot)
{
_dict.Add(id, parameter);
}
}
/// <summary>
/// Gets the specified id.
/// </summary>
/// <typeparam name="T">The type of the tref.</typeparam>
/// <param name="id">The id.</param>
/// <returns>``0.</returns>
/// <resource name="id">The id.</resource>
public T Get<T>(int id)
{
lock (_syncroot)
{
return (T) _dict[id];
}
}
}
/// <summary>
/// Class IModelFactory
/// </summary>
public sealed class ModelFactory
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile ModelFactory _instance;
/// <summary>
/// Prevents a default instance of the <see cref="ModelFactory" /> class from being created.
/// </summary>
private ModelFactory()
{
}
/// <summary>
/// Gets the data.
/// </summary>
/// <value>The data.</value>
public static ModelFactory Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new ModelFactory();
}
}
return _instance;
}
}
/// <summary>
/// Constructs the specified param.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="param">The param.</param>
/// <returns>Model{``0}.</returns>
public Model Construct<T>(T param) where T : IModelable
{
var n = new Model {Resource = param};
return n;
}
}
/// <summary>
/// Class Model
/// </summary>
/// <typeparam name="T"></typeparam>
public class Model
{
public object Resource { get; set; }
}
/// <summary>
/// Interface IModelable
/// </summary>
public interface IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
float Mass { get; set; }
}
/// <summary>
/// Class spaceship
/// </summary>
public class Spaceship : IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
public float Mass { get; set; }
}
So the problem will be lighted here:
Add to the Repository:
Repository.Data.Allocate(1, ModelFactory.Data.Construct(new Spaceship()));
It's okay, but after:
var test_variable = Repository.Data.Get<Model>(1);
So now I have a non type-safe object from a type parameter, I don't know, that what type of class has been stored with the c model construction.
I'm very thankful for the suggestions of using type paramter on the Model class as well, but than it will come up another problem, because I have to change the Get function with it:
var test_variable = Repository.Data.Get<Model<Spaceship>>(1);
But that's definitely wrong, because I won't know, that what kind of type of class has been stored in the model..., so I would like to achieve to avoid this type parameter definition when I would like to load the instance from the Repository.
You can solve this by making your Model class generic, like this:
public class Model<T>
{
public T Resource { get; set; }
}
Then, your Construct method could work like this:
public Model<T> Construct<T>(T param) where T : IModelable<T>
{
return new Model<T>() {Resource = param};
}
You probably need a generic type in the model class:
public class Model<T>
{
public T Resource { get; set; }
}
This sort of structure is one approach you could take:
public Model<T> Construct<T>(T param) where T : IModelable
{
var n = new Model<T> {Resource = param};
return n;
}
public class Model<T> : IModel<T> where T : IModelable
{
public T Resource { get; set; }
}
public interface IModel<out T> where T : IModelable
{
T Resource { get; }
}
This covariant interface allows you to refer to the types more generically where you wish, in the same way that you can pass an IEnumerable<string> into something expecting an IEnumerable<object>.
IModel<Spaceship> shipModel = // whatever
IModel<IModelable> model = shipModel;
//or even:
List<Model<Spaceship>> shipModels = // whatever
IEnumerable<IModel<IModelable>> models = shipModels;
I have a class
public class Setting<T>
{
public string name { get; set; }
public T value { get; set; }
}
now I want to create an IList<Setting<T>> but with different types of Setting<T>'s T in it, I want e.G.
List<Setting<T>> settingsList;
settingsList.Add(new Setting<int>());
settingsList.Add(new Setting<string>());
I've tried IList<Setting<T>> but this seems not possible since the compiler doesn't find Type T.
I know that I could use object but I want it to be strongly typed. So my question is if there is a possibility of getting this working.
Generic types do not have a common type or interface amongst concrete definitions by default.
Have your Setting<T> class implement an interface (or derive from a common class) and create a list of that interface (or class).
public interface ISetting { }
public class Setting<T> : ISetting
{
// ...
}
// example usage:
IList<ISetting> list = new List<ISetting>
{
new Setting<int> { name = "foo", value = 2 },
new Setting<string> { name = "bar", value "baz" },
};
You have to use a common ancestor class for all the class types that you put into the list. If it should be arbitrary types you have to use object for T
You can use T only inside your class:
class Setting<T>
{
// T is defined here
}
not outside. Outside you need to specify the concrete type:
Settings<string> stringSettings = new Settings<string>();
or
List<Settings<string>> list = new List<Settings<string>>();
list.Add(stringSettings);
ye you can do it by using reflection
i wrote such code for another question but you can use this
public abstract class GenericAccess
{
public static IList<T> GetObjectListFromArray<T>(T item)
{
var r = new List<T>();
var obj = typeof(T).Assembly.CreateInstance(typeof(T).FullName);
var p = obj.GetType().GetProperty("Id", System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (p != null)
{
p.SetValue(obj, item, null);
var m = r.GetType().GetMethod("Add");
m.Invoke(r, new object[] { obj });
}
return r;
}
}
and call it like this
IList<int> r = GenericAccess.GetObjectListFromArray<int>(1);
/// <summary>
/// 消息回调委托
/// </summary>
/// <typeparam name="T">TClass</typeparam>
/// <param name="result">返回实体</param>
/// <param name="args">内容包</param>
public delegate void CallbackHandler<in T>(T result, BasicDeliverEventArgs args);
/// <summary>
/// 注册监听程序接口
/// </summary>
public interface IRegistration
{
/// <summary>
/// 路由名称
/// </summary>
string ExchangeName { get; }
/// <summary>
/// 路由Key
/// </summary>
string RouteKey { get; }
/// <summary>
/// 回调操作
/// </summary>
/// <param name="args"></param>
void Call(BasicDeliverEventArgs args);
}
/// <summary>
/// 注册监听程序
/// </summary>
/// <typeparam name="T"></typeparam>
public class Registration<T> : IRegistration where T : class
{
/// <summary>
/// 路由名称
/// </summary>
public string ExchangeName { get; set; }
/// <summary>
/// 路由Key
/// </summary>
public string RouteKey { get; set; }
/// <summary>
/// 消息处理器委托
/// </summary>
public CallbackHandler<T> Handler { get; set; }
/// <summary>
/// 接口回调操作
/// </summary>
/// <param name="args"></param>
public void Call(BasicDeliverEventArgs args)
{
var message = Encoding.UTF8.GetString(args.Body.ToArray());
Handler?.Invoke(JsonConvertHandler.DeserializeObject<T>(message), args);
}
}
/// <summary>
/// 消息监听处理器
/// </summary>
public static class ListenerHandler
{
/// <summary>
/// 单任务锁
/// </summary>
private static readonly object Locker = new object();
/// <summary>
/// 所有注册列表
/// </summary>
public static List<IRegistration> Registrations { get; private set; }
/// <summary>
/// 单例化
/// </summary>
static ListenerHandler()
{
List<ListenerSetting> listeners = MqNoticeHandler.Setting.GetListeners();
MqNoticeHandler.Listener(OnMessage, listeners);
}
/// <summary>
/// 注册监听
/// </summary>
/// <param name="registration"></param>
public static void Register(IRegistration registration)
{
lock (Locker)
Registrations.Add(registration);
}
/// <summary>
/// 解除注册
/// </summary>
/// <param name="registration"></param>
public static void UnRegister(IRegistration registration)
{
lock (Locker)
Registrations.Remove(registration);
}
/// <summary>
/// 获取监听列表
/// </summary>
/// <param name="exchangeName"></param>
/// <param name="routeKey"></param>
/// <returns></returns>
public static IEnumerable<IRegistration> GetRegistrations(string exchangeName, string routeKey)
{
return Registrations.Where(x => x.ExchangeName == exchangeName && x.RouteKey == routeKey);
}
/// <summary>
/// 消息回调处理
/// </summary>
/// <param name="consumer">消费者</param>
/// <param name="args">消息包</param>
/// <returns></returns>
private static MqAckResult OnMessage(EventingBasicConsumer consumer, BasicDeliverEventArgs args)
{
//Example Query and Call
Registrations.First().Call(args);
//Return
return new MqAckResult { Accept = true, ReQueue = false };
}
}