MVC6 Custom IModelBinder, Beta8 changed? - c#

I have this custom IModelBinder that was working, but i have since uninstalled beta5 and beta7, so i am using the latest beta8. It appears that the code has completely changed and i cant seem to find any vNext code on github to see the changes.
Can someone please tell me how to update this to work with beta8, or give me a URL to the source code on github?
public class CommaDelimitedArrayModelBinder : IModelBinder
{
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{
var key = bindingContext.ModelName;
var val = bindingContext.ValueProvider.GetValue(key);
var result = new ModelBindingResult(null, key, false);
if (val != null)
{
var s = val.FirstValue;
if (s != null)
{
var elementType = bindingContext.ModelType.GetElementType();
var converter = TypeDescriptor.GetConverter(elementType);
var values = Array.ConvertAll(s.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries),
x => { return converter.ConvertFromString(x != null ? x.Trim() : x); });
var typedValues = Array.CreateInstance(elementType, values.Length);
values.CopyTo(typedValues, 0);
result = new ModelBindingResult(typedValues, key, true);
}
else
{
// change this line to null if you prefer nulls to empty arrays
result = new ModelBindingResult(Array.CreateInstance(bindingContext.ModelType.GetElementType(), 0), key, true);
}
}
return result;
}
}
The only error i have is that ModelBindResult does not take any constructor parameters and all the parameters are readonly, so they cant be set? So how am i supposed to return a ModelBindingResult, if i cant set any properties on it?

The source code for ModelBindingResult in beta 8 is here. It has static methods that take arguments and return a new instance, for example:
public static ModelBindingResult Success(string key, object model)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return new ModelBindingResult(key, model, isModelSet: true);
}

Related

Calling IF Statement method in C#

I have noticed that I am repeating a lot of my code. What I want to do is store the below if-statement in a method so that it can be called at any time. My problem is, I understand methods, slightly, but I'm having difficulty creating a method for what i want. Below is an example of my code. The commented out code is the code i want to put in a method.Any help is much appreciated.
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
/*if (titleTokens.Contains(s))
{
MessageBox.Show("");
}
else
{
MessageBox.Show("");
}*/
}
private bool Check(string result, string type) {
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
return titleTokens.Contains(s);
}
return false;
}
You mean like this ?
//To call the function use the following line and replace yourClassName with you class name
yourClassName myInstObj= new yourClassName();
myInstObj.Check(result,type);
//Copied from the above answer b/c it should work fine.
private bool Check(string result, string type) {
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
if (titleTokens.Contains(s))
{
return true;
}
else
{
return false;
}
}
}

Search by multiple fields

I'm working on a project and implemented a search by multiple fields in MVC, using LINQ like so:
public ActionResult SearchResult(SearchViewModel model)
{
List<Requisition> emptyList = new List<Requisition>();
if (model.RequisitionID > 0
|| model.Department > 0
|| model.Status > 0
|| model.RequisitionedBy != null)
{
var results = db.Requisitions.Where(x => x.RequisitionId > 0);
results = ProcessSearchInput(model, results);
return PartialView(results.ToList());
}
return PartialView(emptyList);
}
Helper:
private static IQueryable<Requisition> ProcessSearchInput(SearchViewModel model, IQueryable<Requisition> results)
{
if (model.Department > 0)
results = results.Where(x => x.Department == model.Department);
if (model.RequisitionedBy != null)
results = results.Where(x => x.Requisitioned_By.Contains(model.RequisitionedBy));
if (model.Status > 0)
results = results.Where(x => x.Status.Contains(model.Status.ToString()));
return results;
}
This code works fine.
However, if I add an extra search field to the form, I would also need to add a separate if statement in the controller.
With the current approach, the ProcessSearchInput method will contain too many if statements.
Is there a better way to handle a search with multiple fields?
Your current approach violates the open closed principle. The solution is to create a dynamic filter like in this example. However that is a complicated solution that is worth only if you are going to add more and more filters along the way. If not then don't bother.
I agree with previous comments: your current solution is probably the way to go.
In the real world, you'll soon have to implement filters like 'all customers having either a billing- or a shipping-address in New York', and more complicated stuff. By then, all clever generic stuff will just be in the way.
However, if you promise never to use this in production code:
you can save a lot of typing by using a query by example, where you specify the filter as an instance of the type your source contains:
var example = new Requisition { Department = 8, Requisitioned_By ="john" };
var result = db.Requisitions.FilterByExample(example);
This is a simple implementation:
public static class FilterByExampleHelper
{
public static IQueryable<T> FilterByExample<T>(this IQueryable<T> source, T example) where T : class
{
foreach (var property in typeof(T).GetProperties(BindingFlags.Public|BindingFlags.Instance).Where(p => p.CanRead))
{
ConstantExpression valueEx = null;
var propertyType = property.PropertyType;
if (propertyType.IsValueType)
{
var value = property.GetValue(example);
if (value != null &&
!value.Equals(Activator.CreateInstance(propertyType)))
{
valueEx = Expression.Constant(value, propertyType);
}
}
if (propertyType == typeof(string))
{
var value = property.GetValue(example) as string;
if (!string.IsNullOrEmpty(value))
{
valueEx = Expression.Constant(value);
}
}
if (valueEx == null)
{
continue;
}
var parameterEx = Expression.Parameter(typeof(T));
var propertyEx = Expression.Property(parameterEx, property);
var equalsEx = Expression.Equal(propertyEx, valueEx);
var lambdaEx = Expression.Lambda(equalsEx, parameterEx) as Expression<Func<T, bool>>;
source = source.Where(lambdaEx);
}
return source;
}
}

Troubles with large amount of Code duplication

I have a lot of methods that follow in large parts the same algorithm, and I ideally want to be able to make calls to a generic method that eliminates a lot of code duplication.
I have tons of methods like the ones below, I would optimally want to be able to just call
Save<SQLiteLocation>(itemToSave); but i have a great deal of trouble since that the methods
SQLiteConnection.Find<T> won't accept an abstract data type like T in generics.
Is there any way to get around this, if i could get it fixed i would save as many as 150 lines of code
Here's my code:
public bool SaveLocation(ILocation location, ref int primaryKey)
{
var dbConn = new SQLiteConnection (dbPath);
SQLiteLocation itemToSave = new SQLiteLocation ();
itemToSave.LocationName = location.LocationName;
itemToSave.Latitude = location.Latitude;
itemToSave.Longitude = location.Longitude;
itemToSave.PrimaryKey = location.PrimaryKey;
----------------------------------------------------------------------------------------
SQLiteLocation storedLocation = dbConn.Find<SQLiteLocation>
(x => x.PrimaryKey == location.PrimaryKey);
if (storedLocation != null)
{
dbConn.Update(itemToSave);
return true;
}
else if (storedLocation == null)
{
dbConn.Insert(itemToSave);
primaryKey = itemToSave.PrimaryKey;
return true;
}
return false;
}
And here another method see how the code in both methods below my dashed line is basically the same thing
public bool SaveInvitation(IInvitation invitation, ref int primaryKey)
{
var dbConn = new SQLiteConnection(dbPath);
SQLiteInvitation itemToSave = new SQLiteInvitation ();
itemToSave.GroupName = invitation.GroupName;
itemToSave.InviterName = invitation.InviterName;
itemToSave.ParseID = invitation.ParseID;
itemToSave.GroupParseID = invitation.GroupParseID;
itemToSave.PrimaryKey = invitation.PrimaryKey;
---------------------------------------------------------------------------------------
SQLiteInvitation storedInvitation = dbConn.Find<SQLiteInvitation>
(x => x.PrimaryKey == invitation.PrimaryKey);
if (storedInvitation != null)
{
dbConn.Update(itemToSave);
return true;
}
else if (storedInvitation == null)
{
dbConn.Insert(itemToSave);
primaryKey = itemToSave.PrimaryKey;
return true;
}
return false;
}
Shouldn't you be able to do something like this:
Note: ICommonInterface is anything that is common between any allowable classes you would want to use as T. Preferably, looking at your code, an interface or class that exposes the PrimaryKey property.
public bool SaveItem<T>(T item, ref int primaryKey) where T : ICommonInterface, new()
{
var dbConn = new SQLiteConnection(dbPath);
T storedItem = dbConn.Find<T>(x => x.PrimaryKey == item.PrimaryKey);
if (storedItem != null)
{
dbConn.Update(item);
return true;
}
else if (storedItem == null)
{
dbConn.Insert(item);
primaryKey = item.PrimaryKey;
return true;
}
return false;
}
EDIT: Added the new() constraint to the method.
For some reason it threw an not supported exception
when i used :
T storedItem = dbConn.Find<T>(x => x.PrimaryKey == item.PrimaryKey);
The code below fixed my woes though, thanks for the help everyone, I love this site!
Also i added another constraint on T seeing as T has to be a non abstract type and an interface is just that I suppose
private void SaveItem<T>(T item, ref int primaryKey)
where T : ISQLiteClass, new()
{
var dbConn = new SQLiteConnection(dbPath);
T storedItem = dbConn.Find<T>(primaryKey);
if (storedItem != null)
{
dbConn.Update(item);
}
else if (storedItem == null)
{
dbConn.Insert(item);
primaryKey = item.PrimaryKey;
}
}

VS2012 : File Properties; How to specify a default Custom Tool by file type

I'm looking for some mechanism to automatically apply values for File Properties (as seen in the VS Properties Window) when adding a new files of particular types into VS2012 projects.
For example, if someone adds a *.cshtml file, I'd like the Custom Tool to be set by default to 'RazorGenerator' because it can be a hassle ensuring this is specified manually across all razor files.
Similar again for *.spark where the Build Action must always be set to Content (or the CI build breaks).
There doesn't seem to be any VS2012 built in settings for configuring default file properties, so what are people doing to solve this that could work well for a team of developers?
I'm looking for options.
The ANTLR 4 C# target requires similar actions be taken when *.g4 files which are added to the project.
Set the Build Action to Antlr4
Set several additional properties to default values
Expose several custom properties via customization of the Properties window in Visual Studio
Based on your question, I'll focus on only the first item.
Providing a default build action for a file type
This is actually a feature provided as part of the ANTLR Language Support extension. The extension performs the operations when a file with particular extensions is added to the project. See the comment at the beginning of OnAfterAddFilesEx for details of the behavior. This code is from a class which extends Package and implements IVsTrackProjectDocumentsEvents2. The implementation of the remaining methods for this interface simply return VSConstants.E_NOTIMPL. Note that this code may use custom extension methods that are defined elsewhere within my solution. The code is not intended to be used directly, but merely serve as a guide for someone looking to perform a similar operation via their own extension.
private static readonly Guid CSharpProjectTypeGuid = Guid.Parse(PrjKind.prjKindCSharpProject);
private uint _trackDocumentsEventsCookie;
protected override void Initialize()
{
base.Initialize();
IVsTrackProjectDocuments2 trackDocuments2 = serviceProvider.GetTrackProjectDocuments2();
trackDocuments2.AdviseTrackProjectDocumentsEvents(this, out _trackDocumentsEventsCookie);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_trackDocumentsEventsCookie != 0)
{
var serviceProvider = this.AsVsServiceProvider();
IVsTrackProjectDocuments2 trackDocuments = serviceProvider.GetTrackProjectDocuments2();
trackDocuments.UnadviseTrackProjectDocumentsEvents(_trackDocumentsEventsCookie);
_trackDocumentsEventsCookie = 0;
}
}
base.Dispose(disposing);
}
public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)
{
/* need to make the following alterations:
* 1. set the Build Action of *.g and *.g3 to Antlr3
* 2. set the Build Action of *.g4 to Antlr4
* 3. set the Custom Tool of *.g, *.g3, and *.g4 to MSBuild:Compile
* 4. set the Custom Tool Namespace of *.g4 to $(RootNamespace) + relative folder path
*/
for (int i = 0; i < cProjects; i++)
{
IVsProject project = rgpProjects[i];
int projectFiles = (i == cProjects - 1) ? cFiles : rgFirstIndices[i + 1];
projectFiles -= rgFirstIndices[i];
if (!IsCSharpProject(project))
continue;
for (int j = 0; j < projectFiles; j++)
{
string currentFile = rgpszMkDocuments[rgFirstIndices[i] + j];
if (string.IsNullOrEmpty(currentFile))
continue;
bool grammarFile =
currentFile.EndsWith(".tokens", StringComparison.OrdinalIgnoreCase)
|| currentFile.EndsWith(".g", StringComparison.OrdinalIgnoreCase)
|| currentFile.EndsWith(".g3", StringComparison.OrdinalIgnoreCase)
|| currentFile.EndsWith(".g4", StringComparison.OrdinalIgnoreCase);
if (grammarFile)
{
OnAfterAddedGrammarFile(project, currentFile);
}
}
}
return VSConstants.S_OK;
}
private static bool IsCSharpProject(IVsProject project)
{
IVsAggregatableProject aggregatableProject = project as IVsAggregatableProject;
if (aggregatableProject == null)
return false;
string guidsString = null;
if (ErrorHandler.Failed(ErrorHandler.CallWithCOMConvention(() => aggregatableProject.GetAggregateProjectTypeGuids(out guidsString))))
return false;
if (string.IsNullOrWhiteSpace(guidsString))
return false;
string[] guids = guidsString.Split(';');
foreach (var guidString in guids)
{
Guid guid;
if (Guid.TryParse(guidString, out guid) && guid == CSharpProjectTypeGuid)
return true;
}
return false;
}
private void OnAfterAddedGrammarFile(IVsProject project, string currentFile)
{
int found;
VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1];
uint itemId;
if (ErrorHandler.Failed(project.IsDocumentInProject(currentFile, out found, priority, out itemId)))
return;
if (found == 0 || priority[0] != VSDOCUMENTPRIORITY.DP_Standard)
return;
string desiredItemType = "Antlr3";
if (string.Equals(Path.GetExtension(currentFile), ".tokens", StringComparison.OrdinalIgnoreCase))
desiredItemType = "AntlrTokens";
else if (string.Equals(Path.GetExtension(currentFile), ".g4", StringComparison.OrdinalIgnoreCase))
desiredItemType = "Antlr4";
IVsHierarchy hierarchy = project as IVsHierarchy;
if (hierarchy != null)
{
object browseObject = null;
PropertyDescriptorCollection propertyDescriptors = null;
int hr = ErrorHandler.CallWithCOMConvention(() => hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_BrowseObject, out browseObject));
if (ErrorHandler.Succeeded(hr))
propertyDescriptors = TypeDescriptor.GetProperties(browseObject);
object obj;
hr = hierarchy.GetProperty(itemId, (int)__VSHPROPID4.VSHPROPID_BuildAction, out obj);
if (ErrorHandler.Succeeded(hr))
{
string buildAction = obj != null ? obj.ToString() : null;
if (string.IsNullOrWhiteSpace(buildAction) || string.Equals(buildAction, "None", StringComparison.OrdinalIgnoreCase))
{
hr = ErrorHandler.CallWithCOMConvention(() => hierarchy.SetProperty(itemId, (int)__VSHPROPID4.VSHPROPID_BuildAction, desiredItemType));
}
}
if (ErrorHandler.Failed(hr) && propertyDescriptors != null)
{
PropertyDescriptor itemTypeDescriptor = propertyDescriptors["ItemType"] ?? propertyDescriptors["BuildAction"];
if (itemTypeDescriptor != null)
{
obj = itemTypeDescriptor.GetValue(browseObject);
string buildAction = (string)itemTypeDescriptor.Converter.ConvertToInvariantString(obj);
if (string.IsNullOrWhiteSpace(buildAction) || string.Equals(buildAction, "None", StringComparison.OrdinalIgnoreCase))
{
try
{
obj = itemTypeDescriptor.Converter.ConvertFromInvariantString(desiredItemType);
itemTypeDescriptor.SetValue(browseObject, obj);
}
catch (NotSupportedException)
{
}
}
}
}
if (propertyDescriptors != null)
{
PropertyDescriptor customToolDescriptor = propertyDescriptors["CustomTool"];
if (customToolDescriptor != null)
{
obj = customToolDescriptor.GetValue(browseObject);
string customTool = customToolDescriptor.Converter.ConvertToInvariantString(obj);
if (string.IsNullOrWhiteSpace(customTool))
{
try
{
obj = customToolDescriptor.Converter.ConvertToInvariantString("MSBuild:Compile");
customToolDescriptor.SetValue(browseObject, obj);
}
catch (NotSupportedException)
{
}
}
}
PropertyDescriptor customToolNamespaceDescriptor = propertyDescriptors["CustomToolNamespace"];
if (customToolNamespaceDescriptor != null)
{
object defaultNamespace;
hr = hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_DefaultNamespace, out defaultNamespace);
if (ErrorHandler.Succeeded(hr) && !string.IsNullOrEmpty(defaultNamespace as string))
{
obj = customToolNamespaceDescriptor.GetValue(browseObject);
string customToolNamespace = customToolNamespaceDescriptor.Converter.ConvertToInvariantString(obj);
if (string.IsNullOrWhiteSpace(customToolNamespace))
{
try
{
obj = customToolNamespaceDescriptor.Converter.ConvertToInvariantString(defaultNamespace);
customToolNamespaceDescriptor.SetValue(browseObject, obj);
}
catch (NotSupportedException)
{
}
}
}
}
}
}
}

Generic extension method for retrieving 'default' value?

I'm playing around a bit with determining the default values of objects, based on the example here:
https://stackoverflow.com/a/3195792/1293496
This particular extension method was created for System.Type. What I was trying to accomplish was to make this even more generic, where I could do something like this:
int i = 3;
bool amIaDefaultValue = i.IsDefaultValue();
I would expect this to return true if i == 0 (the default value for an int), and false for all other instances.
Here is my initial attempt:
public static bool IsDefaultValue<T>(this T value)
{
var t = typeof(T); // always comes back as object..?
if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
{
return value.Equals(Activator.CreateInstance<T>());
}
else
{
var defaultValue = default(T);
if (value == null)
return defaultValue == null;
else
return value.Equals(defaultValue);
}
}
On the plus side, I'm able to attach .IsDefaultValue() to any object. Unfortunately, the type of T always comes back as System.Object. I can get the correct type if I set it up this way:
var t = typeof(value);
But if the value happens to be null, I'll get an error straight away. Is there a good workaround for implementing an extension method like this? Or should I stick to the tried and tested route from the example?
Edit
As pointed out by comments, it seems I oversimplified this a bit and missed the root of the problem. Here's what was actually calling my IsDefaultValue():
foreach (var imprintProperty in deltas.GetType().GetProperties())
{
var value = imprintProperty.GetValue(deltas);
if (!value.IsDefaultValue())
{
// get corresponding prop in programmable area
var programmableProp = progarea.GetType().GetProperty(imprintProperty.Name);
if (programmableProp != null)
programmableProp.SetValue(progarea, value);
}
}
And now it becomes obvious that .GetValue is always returning as System.Object. Uff.
Is it still possible to treat the object as its underlying type in the extension method? Sorry for the confusion with this.
Take a look at this:
static class Program
{
static void Main()
{
int a = 1;
Console.WriteLine("a.IsDefaultValue() : " + a.IsDefaultValue());
a = 0;
Console.WriteLine("a.IsDefaultValue() : " + a.IsDefaultValue());
object obj = new object();
Console.WriteLine("obj.IsDefaultValue() : " + obj.IsDefaultValue());
obj = null;
Console.WriteLine("obj.IsDefaultValue() : " + obj.IsDefaultValue());
int? b = 1;
Console.WriteLine("b.IsDefaultValue() : " + b.IsDefaultValue());
b = null;
Console.WriteLine("b.IsDefaultValue() : " + b.IsDefaultValue());
Console.ReadKey(true);
}
static bool IsDefaultValue<T>(this T value)
{
if (ReferenceEquals(value, null))
{
return true;
}
var t = value.GetType();
if (t.IsValueType)
{
return value.Equals(Activator.CreateInstance(value.GetType()));
}
return false;
}
}
Apparently works (I've to say that I was convinced that the other way should have worked but not)

Categories