8#if NETSTANDARD2_0 || NET5_0 || NETCORE3_1
10using System.Collections;
11using System.Collections.Generic;
13using System.Threading.Tasks;
14using Microsoft.AspNetCore.Mvc.ModelBinding;
22 public class CslaModelBinder : Server.ObjectFactory, IModelBinder
30 _instanceCreator = instanceCreator;
38 public CslaModelBinder(Func<Type, Task<object>> instanceCreator, Func<IList, Type, Dictionary<string, string>,
object> childCreator)
40 _instanceCreator = instanceCreator;
41 _childCreator = childCreator;
44 private readonly Func<Type, Task<object>> _instanceCreator;
45 private readonly Func<IList, Type, Dictionary<string, string>,
object> _childCreator;
51 public async Task BindModelAsync(ModelBindingContext bindingContext)
53 if (bindingContext ==
null)
55 throw new ArgumentNullException(nameof(bindingContext));
58 var result = await _instanceCreator(bindingContext.ModelType);
62 if (typeof(Core.IEditableCollection).IsAssignableFrom(bindingContext.ModelType))
64 BindBusinessListBase(bindingContext, result);
66 else if (typeof(Core.IEditableBusinessObject).IsAssignableFrom(bindingContext.ModelType))
68 BindBusinessBase(bindingContext, result);
75 bindingContext.Result = ModelBindingResult.Success(result);
79 private void BindBusinessBase(ModelBindingContext bindingContext,
object result)
81 var properties = Core.FieldManager.PropertyInfoManager.GetRegisteredProperties(bindingContext.ModelType);
82 foreach (var item
in properties)
85 if (
string.IsNullOrEmpty(bindingContext.ModelName))
86 index = $
"{item.Name}";
88 index = $
"{bindingContext.ModelName}.{item.Name}";
89 BindSingleProperty(bindingContext, result, item, index);
94 private void BindBusinessListBase(ModelBindingContext bindingContext,
object result)
96 var formKeys = bindingContext.ActionContext.HttpContext.Request.Form.Keys.Where(_ => _.StartsWith(bindingContext.ModelName));
97 var childType = Utilities.GetChildItemType(bindingContext.ModelType);
98 var properties = Core.FieldManager.PropertyInfoManager.GetRegisteredProperties(childType);
99 var list = (IList)result;
101 var itemCount = formKeys.Count() / properties.Count();
102 for (
int i = 0; i < itemCount; i++)
104 var child = _childCreator(
106 GetFormValuesForObject(bindingContext.ActionContext.HttpContext.Request.Form, bindingContext.ModelName, i, properties));
109 throw new InvalidOperationException($
"Could not create instance of child type {childType}");
110 foreach (var item
in properties)
112 var index = $
"{bindingContext.ModelName}[{i}].{item.Name}";
113 BindSingleProperty(bindingContext, child, item, index);
116 if (!list.Contains(child))
121 private Dictionary<string, string> GetFormValuesForObject(
122 Microsoft.AspNetCore.Http.IFormCollection formData,
125 Core.FieldManager.PropertyInfoList properties)
127 var result =
new Dictionary<string, string>();
128 foreach (var item
in properties)
130 var key = $
"{modelName}[{index}].{item.Name}";
131 result.Add(item.Name, formData[key]);
136 private void BindSingleProperty(ModelBindingContext bindingContext,
object result, Core.IPropertyInfo item,
string index)
140 var value = bindingContext.ActionContext.HttpContext.Request.Form[index].FirstOrDefault();
143 if (item.Type.Equals(typeof(
string)))
144 Reflection.MethodCaller.CallPropertySetter(result, item.Name, value);
145 else if (value !=
null)
146 Reflection.MethodCaller.CallPropertySetter(result, item.Name, Utilities.CoerceValue(item.Type, value.GetType(),
null, value));
148 Reflection.MethodCaller.CallPropertySetter(result, item.Name,
null);
152 if (item.Type.Equals(typeof(
string)))
153 LoadProperty(result, item, value);
154 else if (value !=
null)
155 LoadProperty(result, item, Utilities.CoerceValue(item.Type, value.GetType(),
null, value));
157 LoadProperty(result, item,
null);
162 throw new Exception($
"Could not map {index} to model", ex);
171 public class CslaModelBinderProvider : IModelBinderProvider
177 public CslaModelBinderProvider()
178 : this(CreateInstance, CreateChild)
187 public CslaModelBinderProvider(Func<Type, Task<object>> instanceCreator, Func<IList, Type, Dictionary<string, string>,
object> childCreator)
189 _instanceCreator = instanceCreator;
190 _childCreator = childCreator;
193 internal static Task<object> CreateInstance(Type type)
195 var tcs =
new TaskCompletionSource<object>();
196 tcs.SetResult(Reflection.MethodCaller.CreateInstance(type));
200 internal static object CreateChild(IList parent, Type type, Dictionary<string, string> values)
202 return Reflection.MethodCaller.CreateInstance(type);
205 private readonly Func<Type, Task<object>> _instanceCreator;
206 private readonly Func<IList, Type, Dictionary<string, string>,
object> _childCreator;
212 public IModelBinder GetBinder(ModelBinderProviderContext context)
214 if (typeof(Core.IEditableCollection).IsAssignableFrom(context.Metadata.ModelType))
215 return new CslaModelBinder(_instanceCreator, _childCreator);
216 if (typeof(IBusinessBase).IsAssignableFrom(context.Metadata.ModelType))
217 return new CslaModelBinder(_instanceCreator);
224using System.Collections.Generic;
227using System.ComponentModel;
228using System.Collections;
238 private bool _checkRulesOnModelUpdated;
246 _checkRulesOnModelUpdated = CheckRulesOnModelUpdated;
255 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
257 if (typeof(Core.IEditableCollection).IsAssignableFrom((bindingContext.ModelType)))
258 return BindCslaCollection(controllerContext, bindingContext);
260 var suppress = bindingContext.Model as Core.ICheckRules;
261 if (suppress !=
null)
262 suppress.SuppressRuleChecking();
263 var result = base.BindModel(controllerContext, bindingContext);
273 private object BindCslaCollection(ControllerContext controllerContext, ModelBindingContext bindingContext)
275 if (bindingContext.Model ==
null)
276 bindingContext.ModelMetadata.Model =
CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
278 var collection = (IList)bindingContext.Model;
279 for (
int currIdx = 0; currIdx < collection.Count; currIdx++)
281 string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currIdx);
282 if (!bindingContext.ValueProvider.ContainsPrefix(subIndexKey))
284 var elementModel = collection[currIdx];
285 var suppress = elementModel as Core.ICheckRules;
286 if (suppress !=
null)
287 suppress.SuppressRuleChecking();
288 var elementContext =
new ModelBindingContext()
290 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => elementModel, elementModel.GetType()),
291 ModelName = subIndexKey,
292 ModelState = bindingContext.ModelState,
293 PropertyFilter = bindingContext.PropertyFilter,
294 ValueProvider = bindingContext.ValueProvider
297 if (OnModelUpdating(controllerContext, elementContext))
300 foreach (PropertyDescriptor property
in GetFilteredModelProperties(controllerContext, elementContext))
302 BindProperty(controllerContext, elementContext, property);
308 return bindingContext.Model;
318 protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
320 var controller = controllerContext.Controller as
IModelCreator;
321 if (controller !=
null)
324 return base.CreateModel(controllerContext, bindingContext, modelType);
333 protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
335 var obj = bindingContext.Model as Core.BusinessBase;
338 if (this._checkRulesOnModelUpdated)
340 var suppress = obj as Core.ICheckRules;
341 if (suppress !=
null)
343 suppress.ResumeRuleChecking();
344 suppress.CheckRules();
347 var errors = from r in obj.BrokenRulesCollection
348 where r.Severity == Rules.RuleSeverity.Error
350 foreach (var item
in errors)
353 string mskey = CreateSubPropertyName(bindingContext.ModelName, item.Property ??
string.Empty);
354 if (bindingContext.ModelState.TryGetValue(mskey, out state))
356 if (state.Errors.Where(e => e.ErrorMessage == item.Description).Any())
359 bindingContext.ModelState.AddModelError(mskey, item.Description);
361 else if (mskey ==
string.Empty)
362 bindingContext.ModelState.AddModelError(bindingContext.ModelName, item.Description);
367 base.OnModelUpdated(controllerContext, bindingContext);
378 protected override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor,
object value)
380 if (!(bindingContext.Model is Core.BusinessBase))
381 base.OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, value);
Model binder for use with CSLA .NET editable business objects.
override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
Creates an instance of the model if the controller implements IModelCreator.
CslaModelBinder(bool CheckRulesOnModelUpdated=true)
Creates an instance of the model binder.
override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
Binds the model by using the specified controller context and binding context.
override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
Checks the validation rules for properties after the Model has been updated.
override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
Prevents IDataErrorInfo validation from operating against editable objects.
ASP.NET MVC model creator.
object CreateModel(Type modelType)
Creates a model object of the specified type.
Defines a CSLA .NET MVC viewmodel object.
@ CheckRules
Called from CheckRules