CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
CslaModelBinder.cs
Go to the documentation of this file.
1//-----------------------------------------------------------------------
2// <copyright file="CslaModelBinder.cs" company="Marimer LLC">
3// Copyright (c) Marimer LLC. All rights reserved.
4// Website: https://cslanet.com
5// </copyright>
6// <summary>Model binder for use with CSLA .NET editable business objects.</summary>
7//-----------------------------------------------------------------------
8#if NETSTANDARD2_0 || NET5_0_OR_GREATER || NETCOREAPP3_1
9using System;
10using System.Collections;
11using System.Linq;
12using System.Threading.Tasks;
13using Microsoft.AspNetCore.Mvc.ModelBinding;
14
15namespace Csla.Web.Mvc
16{
21 public class CslaModelBinder : Server.ObjectFactory, IModelBinder
22 {
26 public CslaModelBinder()
27 : base(null) { }
28
33 public Task BindModelAsync(ModelBindingContext bindingContext)
34 {
35 ApplicationContext = (ApplicationContext)bindingContext.HttpContext.RequestServices.GetService(typeof(ApplicationContext));
36 if (bindingContext == null)
37 {
38 throw new ArgumentNullException(nameof(bindingContext));
39 }
40
41 bindingContext.Result = ModelBindingResult.Failed();
42 var result = ApplicationContext.CreateInstanceDI(bindingContext.ModelType);
43 if (result != null)
44 {
45 if (typeof(Core.IEditableCollection).IsAssignableFrom(bindingContext.ModelType))
46 {
47 BindBusinessListBase(bindingContext, result);
48 bindingContext.Result = ModelBindingResult.Success(result);
49 }
50 else if (typeof(Core.IEditableBusinessObject).IsAssignableFrom(bindingContext.ModelType))
51 {
52 BindBusinessBase(bindingContext, result);
53 bindingContext.Result = ModelBindingResult.Success(result);
54 }
55 }
56 return Task.CompletedTask;
57 }
58
59 private void BindBusinessBase(ModelBindingContext bindingContext, object result)
60 {
61 var properties = Core.FieldManager.PropertyInfoManager.GetRegisteredProperties(bindingContext.ModelType);
62 foreach (var item in properties)
63 {
64 string index;
65 if (string.IsNullOrEmpty(bindingContext.ModelName))
66 index = $"{item.Name}";
67 else
68 index = $"{bindingContext.ModelName}.{item.Name}";
69 BindSingleProperty(bindingContext, result, item, index);
70 }
71 CheckRules(result);
72 }
73
74 private void BindBusinessListBase(ModelBindingContext bindingContext, object result)
75 {
76 var applicationContext = (ApplicationContext)bindingContext.HttpContext.RequestServices.GetService(typeof(ApplicationContext));
77 var formKeys = bindingContext.ActionContext.HttpContext.Request.Form.Keys.Where(_ => _.StartsWith(bindingContext.ModelName));
78 var childType = Utilities.GetChildItemType(bindingContext.ModelType);
79 var properties = Core.FieldManager.PropertyInfoManager.GetRegisteredProperties(childType);
80 var list = (IList)result;
81
82 var itemCount = formKeys.Count() / properties.Count();
83 for (int i = 0; i < itemCount; i++)
84 {
85 var child = applicationContext.CreateInstanceDI(childType);
86 MarkAsChild(child);
87 if (child == null)
88 throw new InvalidOperationException($"Could not create instance of child type {childType}");
89 foreach (var item in properties)
90 {
91 var index = $"{bindingContext.ModelName}[{i}].{item.Name}";
92 BindSingleProperty(bindingContext, child, item, index);
93 }
94 CheckRules(child);
95 if (!list.Contains(child))
96 list.Add(child);
97 }
98 }
99
100 private void BindSingleProperty(ModelBindingContext bindingContext, object result, Core.IPropertyInfo item, string index)
101 {
102 try
103 {
104 var value = bindingContext.ActionContext.HttpContext.Request.Form[index].FirstOrDefault();
105 try
106 {
107 if (item.Type.Equals(typeof(string)))
108 Reflection.MethodCaller.CallPropertySetter(result, item.Name, value);
109 else if (value != null)
110 Reflection.MethodCaller.CallPropertySetter(result, item.Name, Utilities.CoerceValue(item.Type, value.GetType(), null, value));
111 else
112 Reflection.MethodCaller.CallPropertySetter(result, item.Name, null);
113 }
114 catch
115 {
116 if (item.Type.Equals(typeof(string)))
117 LoadProperty(result, item, value);
118 else if (value != null)
119 LoadProperty(result, item, Utilities.CoerceValue(item.Type, value.GetType(), null, value));
120 else
121 LoadProperty(result, item, null);
122 }
123 }
124 catch (Exception ex)
125 {
126 throw new Exception($"Could not map {index} to model", ex);
127 }
128 }
129 }
130}
131#elif !NETSTANDARD
132using System;
133using System.Collections.Generic;
134using System.Linq;
135using System.Web.Mvc;
136using System.ComponentModel;
137using System.Collections;
138
139namespace Csla.Web.Mvc
140{
145 public class CslaModelBinder : DefaultModelBinder
146 {
147 private bool _checkRulesOnModelUpdated;
148
153 public CslaModelBinder(bool CheckRulesOnModelUpdated = true)
154 {
155 _checkRulesOnModelUpdated = CheckRulesOnModelUpdated;
156 }
157
164 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
165 {
166 if (typeof(Core.IEditableCollection).IsAssignableFrom((bindingContext.ModelType)))
167 return BindCslaCollection(controllerContext, bindingContext);
168
169 var suppress = bindingContext.Model as Core.ICheckRules;
170 if (suppress != null)
171 suppress.SuppressRuleChecking();
172 var result = base.BindModel(controllerContext, bindingContext);
173 return result;
174 }
175
182 private object BindCslaCollection(ControllerContext controllerContext, ModelBindingContext bindingContext)
183 {
184 if (bindingContext.Model == null)
185 bindingContext.ModelMetadata.Model = CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
186
187 var collection = (IList)bindingContext.Model;
188 for (int currIdx = 0; currIdx < collection.Count; currIdx++)
189 {
190 string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currIdx);
191 if (!bindingContext.ValueProvider.ContainsPrefix(subIndexKey))
192 continue; //no value to update skip
193 var elementModel = collection[currIdx];
194 var suppress = elementModel as Core.ICheckRules;
195 if (suppress != null)
196 suppress.SuppressRuleChecking();
197 var elementContext = new ModelBindingContext()
198 {
199 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => elementModel, elementModel.GetType()),
200 ModelName = subIndexKey,
201 ModelState = bindingContext.ModelState,
202 PropertyFilter = bindingContext.PropertyFilter,
203 ValueProvider = bindingContext.ValueProvider
204 };
205
206 if (OnModelUpdating(controllerContext, elementContext))
207 {
208 //update element's properties
209 foreach (PropertyDescriptor property in GetFilteredModelProperties(controllerContext, elementContext))
210 {
211 BindProperty(controllerContext, elementContext, property);
212 }
213 OnModelUpdated(controllerContext, elementContext);
214 }
215 }
216
217 return bindingContext.Model;
218 }
219
227 protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
228 {
229 var controller = controllerContext.Controller as IModelCreator;
230 if (controller != null)
231 return controller.CreateModel(modelType);
232 else
233 return base.CreateModel(controllerContext, bindingContext, modelType);
234 }
235
242 protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
243 {
244 var obj = bindingContext.Model as Core.BusinessBase;
245 if (obj != null)
246 {
247 if (this._checkRulesOnModelUpdated)
248 {
249 var suppress = obj as Core.ICheckRules;
250 if (suppress != null)
251 {
252 suppress.ResumeRuleChecking();
253 suppress.CheckRules();
254 }
255 }
256 var errors = from r in obj.BrokenRulesCollection
257 where r.Severity == Rules.RuleSeverity.Error
258 select r;
259 foreach (var item in errors)
260 {
261 ModelState state;
262 string mskey = CreateSubPropertyName(bindingContext.ModelName, item.Property ?? string.Empty);
263 if (bindingContext.ModelState.TryGetValue(mskey, out state))
264 {
265 if (state.Errors.Where(e => e.ErrorMessage == item.Description).Any())
266 continue;
267 else
268 bindingContext.ModelState.AddModelError(mskey, item.Description);
269 }
270 else if (mskey == string.Empty)
271 bindingContext.ModelState.AddModelError(bindingContext.ModelName, item.Description);
272 }
273 }
274 else
275 if (!(bindingContext.Model is IViewModel))
276 base.OnModelUpdated(controllerContext, bindingContext);
277 }
278
287 protected override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
288 {
289 if (!(bindingContext.Model is Core.BusinessBase))
290 base.OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, value);
291 }
292 }
293}
294#endif
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