conversion Error from String to System.Web.Mvc.ViewResult in MVC3 - asp.net-mvc-3

Hi I am quite new on MVC and I am trying to create a simple conversion from Fahrenheit to Celsius along with its unit testing. Sorry in advance for putting all the code here.
This is my controller code:
public string Convert(double value,string option)
{
string d;
if(option=="1") {
d = " To Celcius"+FahrenheitToCelsius(value).ToString();
}
else {
d = " To Fahrenheit" + CelsiusToFahrenheit(value).ToString();
}
return "ConvertTo" + d;
}
public static double CelsiusToFahrenheit(double temperatureCelsius)
{
double celsius = temperatureCelsius;
return (celsius * 9 / 5) + 32;
}
public static double FahrenheitToCelsius (double temperatureFahrenheit)
{
double fahrenheit = temperatureFahrenheit;
return (fahrenheit - 32) * 5 / 9;
}
This is my View Page
protected void btnConvert(object sender, EventArgs e)
{
if (DropDownList1.SelectedValue=="1"){
double temp = TemperatureConverterController.FahrenheitToCelsius(double.Parse(TextBox1.Text));
Literal1.Text = temp.ToString();
}
else{
double temp = TemperatureConverterController.CelsiusToFahrenheit(double.Parse(TextBox1.Text));
Literal1.Text = temp.ToString();
Literal1.Text = temp.ToString();
}
}
When i do this unit testing i got an error:
[TestMethod]
public void ConvertReturnsAViewResultWhenInputDataIsValid()
{
//Arrange
var controller = new TemperatureConverterController();
//Act
double x = 80;
double y = 25;
var result = controller.Convert(x, "1") as ViewResult;
// here i get this error under ViewResult //
//Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
[TestMethod]
public void ConvertAsksForAViewTemplateNamedConvert()
{
//Arrange
var controller = new TemperatureConverterController();
String expectedViewTemplate = "Convert";
//Act
double x = 80;
double y = 25;
var result = controller.Convert(x, "1") as ViewResult;
////Assert
Assert.AreEqual<String>(expectedViewTemplate, result.ViewName);
}
Error is:
Error Cannot convert type 'string' to 'System.Web.Mvc.ViewResult' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion.

the problem is here
var result = controller.Convert(x, "1") as ViewResult;
your Convert method is returning string and you are casting it as ViewResult
Your convert method should looks like
public ActionResult Convert()
{
//Make a Model class and pass it to View
//...
return View(model_class_object);
}
Alternatively you can make controller like this
public ActionResult Convert()
{
ViewData["tempvalue"]=Convert(x, "1");
//Make a Model class and pass it to View
//...
return View();
}
and on your View you can just print it
#ViewData["tempvalue"].ToString()

In MVC the controller code should return an "ActionResult" object containing the model.
If the data you want to pass to view is simply a string use:
public ActionResult Convert()
{
//...
return View("your result here...");
}
You can refer to data returned by controller using the "Model" property in Views or Tests.

Let's go backwards for a minute here.
Controller
public class ConvertController : Controller
{
public ActionResult Convert(MyConvertViewModel vm)
{
if (vm == null) { return View("convert", new MyConvertViewModel { ShowResult = false }); }
if (vm.Option == 1)
{
vm.Result = FahrenheitToCelsius(vm.Input);
vm.OptionName = "Fahrenheit To Celsius";
}
else
{
vm.Result = CelsiusToFahrenheit(vm.Input);
vm.OptionName = "Celsius to Fahrenheit";
}
vm.ShowResult = true;
//not needed, just for an example
ViewData.Add("glosrob-example", "A value goes here!");
return View("convert", vm);
}
private static double CelsiusToFahrenheit(double temperatureCelsius)
{
double celsius = temperatureCelsius;
return (celsius * 9 / 5) + 32;
}
private static double FahrenheitToCelsius(double temperatureFahrenheit)
{
double fahrenheit = temperatureFahrenheit;
return (fahrenheit - 32)*5/9;
}
}
public class MyConvertViewModel
{
public double Result { get; set; }
public int Option { get; set; }
public double Input { get; set; }
public string OptionName { get; set; }
public bool ShowResult { get; set; }
}
View
#model MvcApplication1.Controllers.MyConvertViewModel
#{
ViewBag.Title = "Convert";
}
<h2>Convert</h2>
#using (Html.BeginForm("convert", "convert", FormMethod.Post))
{
<div>
Let's convert some temperatures!
</div>
<div>
#Html.LabelFor(x => x.Input, "Temp. To Convert")
#Html.TextBoxFor(x => x.Input)
</div>
<div>
#Html.LabelFor(x => x.Option, "Convert to ")
#Html.DropDownListFor(x => x.Option, new List<SelectListItem>
{
new SelectListItem {Text = "Celsius", Value = "1"},
new SelectListItem {Text = "Fahrenheit", Value = "2"}
})
</div>
<div>
<button type="submit">Convert It!</button>
</div>
}
#if (Model.ShowResult)
{
<p>#Model.OptionName : #Model.Input = #Model.Result</p>
}
disclaimer: there is a lot of shortcuts there, it is only included to give you an idea of what you should have.
So the view will post back data the user chooses, to the controller action Convert
The controller in turn will return a ViewResult object, and it will be rendered using the data captured in the view model MyConvertViewModel
Now we want to test this.
So here are some of the more important properties that it seems like you need to hook into
[TestMethod]
public void Not_A_Real_Test_But_Stuff_You_Will_Want_To_Use()
{
//arrange
var c = new ConvertController();
//act
var results = c.Convert(null) as ViewResult;
//now results is a ViewResult or null
var theViewModelProperty = results.Model as MyConvertViewModel;
var exampleResult = theViewModelProperty.Result;
var exampleInput = theViewModelProperty.Input;
//etc
//how about the view that was returned?
var theViewName = results.ViewName;
//or anything you put in the ViewData
var theViewData = results.ViewData["glosrob-example"];
Assert.Fail("This was not a real test!");
}
Hopefully this gives you an idea of how you can test for output from a controller method.
Edit: I'm not writing all your tests for you but as an e.g.
[TestMethod]
public void Convert_Should_Return_A_MyConvertViewModel()
{
//arrange
var c = new Controller();
//act
var result = c.Convert(null) as ViewResult;
//assert
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result.ViewModel, typeof(MyConvertViewModel));
}
[TestMethod]
public void Convert_Should_Return_The_Correct_View()
{
//arrange
var c = new Controller();
//act
var result = c.Convert(null) as ViewResult;
//assert
Assert.IsNotNull(result);
Assert.AreEqual("convert", result.ViewName);
}

Related

Accessing elements of types with runtime indexers using expression trees

I want to validate rule against input array data with runtime indexer not with some fixed zero index value.
It works if i insert data one by one in session.Insert()
It does't work if i use sesion.InsertAll() with array data
I tried providing Expression.Constant value to indexer but no action triggers
class Program
{
static void Main(string[] args)
{
RuleTestWithSingleInsertData();
// RuleTestWithInsertDataAll();
Console.ReadKey();
}
public static void RuleTestWithSingleInsertData()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest1(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 19 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void RuleTestWithInsertDataAll()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest2(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
public class RuleEngineRequestModel
{
public List<RuleEngineEntity> ruleList { get; set; }
public Customer[] customerData { get; set; }
public List<Customer> customerDataList { get; set; }
}
public class RuleEngineEntity
{
public string Name { get; set; }
public int Value { get; set; }
public string Operator { get; set; }
public string FieldName { get; set; }
public bool SendEmail { get; set; }
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CustomRuleRepository : IRuleRepository
{
private readonly IRuleSet _ruleSet = new RuleSet("customerRule");
public IEnumerable<IRuleSet> GetRuleSets()
{
return new[] { _ruleSet };
}
public void LoadRuleForTest1(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest1(rule));
}
public void LoadRuleForTest2(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest2(rule));
}
public List<IRuleDefinition> BuildRuleForTest1(RuleEngineEntity rule)
{
return Test1(rule);
}
public List<IRuleDefinition> BuildRuleForTest2(RuleEngineEntity rule)
{
return Test2(rule);
}
public List<IRuleDefinition> Test1(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(Customer), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var expres = Expression.Property(modelParameter, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer, RuleEngineEntity>> action =
(ctx, CustomerCheck, rules) => FireActionAsync(ctx, CustomerCheck, rules);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public List<IRuleDefinition> Test2(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(RuleEngineRequestModel), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var customerDataInArray = Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData));
var indx = Expression.Parameter(typeof(int), "index");
var customerData = Expression.ArrayIndex(customerDataInArray, indx);
var expres = Expression.Property(customerData, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer>> action =
(ctx, CustomerCheck) => FireActionAsync(ctx, CustomerCheck, null);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public void FireActionAsync(IContext ctx, Customer customer, RuleEngineEntity rule=null)
{
Console.WriteLine($"{rule.Name} Triggered");
}
}
Error: variable 'index' of type 'System.Int32' referenced from scope '', but it is not defined
Expected: want to validate rule against array data with dynamic indexer.
At a quick glance, it appears that you are passing in an array of Customers when inserting the facts into the session, but the rule is looking for the RuleEngineRequestModel.
Also, side note - why are you initialising an array as a List, then converting to an array, rather than just initialising as an array? i.e.
var ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = {
new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 }
}
};
Finally, why are you inserting the rules at the same time as the data? That seems like it would cause more headaches than benefits, especially seeing as your runtime rule builder ignores them entirely.
EDIT: Having had a chance to see the updated code, this confirms my suspicions - if you use Rule 1 for both tests, it should work (rather, it will fire for each individual customer). The reason why Rule 2 never works is because it's expecting a RuleEngineRequestModel, but you are passing the IEnumerable in directly. Either pass in the request model directly and continue to use Rule 2, or scrap Rule 2 entirely.

Does the IClientValidator support input file?

Edit
I found that the problem is that View Components are unable to have an #section (see ViewComponent and #Section #2910 ) so adding custom client-side validation using the unobtrusive library seems imposible (or very complex). Moreover, the inability of including the required javascript into a View Component makes me regret of following this approach to modularize my app in the first place...
I am learning to make custom validation attributes with client-side support. I was able to implement a custom validator for a string property and it works pretty well, but when I tried to make one for input file it doesn't work (i.e. when I select a file in my computer, the application doesn't display the validation messages. The server-side validation works. Here is some code that shows my implementation.
The class of the model
public class UploadPanelModel
{
public int? ID { get; set; }
public string Title { get; set; }
public string Description { get; set; } //Raw HTML with the panel description
[FileType(type: "application/pdf")]
[FileSize(maxSize: 5000000)]
public IFormFile File { get; set; }
public byte[] FileBytes { get; set; }
public ModalModel Modal { get; set; } //Only used if the Upload panel uses a modal.
The validator
public class FileSizeAttribute : ValidationAttribute, IClientModelValidator
{
private long _MaxSize { get; set; }
public FileSizeAttribute (long maxSize)
{
_MaxSize = maxSize;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
UploadPanelModel panel = (UploadPanelModel)validationContext.ObjectInstance;
return (panel.File==null || panel.File.Length <= _MaxSize) ? ValidationResult.Success : new ValidationResult(GetFileSizeErrorMessage(_MaxSize));
}
private string GetFileSizeErrorMessage(long maxSize)
{
double megabytes = maxSize / 1000000.0;
return $"El archivo debe pesar menos de {megabytes}MB";
}
public void AddValidation(ClientModelValidationContext context)
{
if(context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-filesize", GetFileSizeErrorMessage(_MaxSize));
var maxSize = _MaxSize.ToString();
MergeAttribute(context.Attributes, "data-val-filesize-maxsize", maxSize);
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
The javascript in the Razor View
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('filesize',
function (value, element, params) {
var size = $((params[0]).val()).size(),
maxSize = params[1];
if (size < maxSize) {
return false;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('filesize',
['maxSize'],
function (options) {
var element = $(options.form).find('input#File')[0];
options.rules['filesize'] = [element, options.params['maxSize']];
options.messages['filesize'] = options.message;
}
);
</script>
I always return false in the javascript method to force the application to show the validation error regardless the chosen file, but it still doesn't work.
Your addMethod() function will be throwing an error because params[0] is not a jQuery object and has no .val() (you also have the $ in the wrong place). You would need to use
var size = params[0].files[0].size;
However I suggest you write you scripts as
$.validator.unobtrusive.adapters.add('filesize', ['maxsize'], function (options) {
options.rules['filesize'] = { maxsize: options.params.maxsize };
if (options.message) {
options.messages['filesize'] = options.message;
}
});
$.validator.addMethod("filesize", function (value, element, param) {
if (value === "") {
return true;
}
var maxsize = parseInt(param.maxsize);
if (element.files != undefined && element.files[0] != undefined && element.files[0].size != undefined) {
var filesize = parseInt(element.files[0].size);
return filesize <= maxsize ;
}
return true; // in case browser does not support HTML5 file API
});

Model binding issues with Kendo complex object

My problem is very similar to this Model binding issues with Kendo objects with complex child properties . The only difference is that i have another level in the object.
My model is:
Public Person
{
public int Id {get;set;}
public string Name {get;set;}
public IEnumerable<Course> Courses {get;set;}
}
public Course
{
public int Id {get;set;}
public string Description {get;set;}
public IEnumerable<Schedule> Schedules {get;set;}
}
Public Schedule
{
public DateTime Init {get;set;}
public DateTime End {get;set;}
}
This model is bound to a KendoGrid. Everything works well, except that Init and End properties are always null when I posted the model.
In the Ajax Datasource :
.Update(update => update.Action("Update", "Controller").Data("serialize"))
.Create(create => create.Action("Create", "Controller").Data("serialize"))
<script>
function serialize(data) {
for (var property in data) {
if ($.isArray(data[property])) {
serializeArray(property, data[property], data);
}
}
};
function serializeArray(prefix, array, result) {
for (var i = 0; i < array.length; i++) {
if ($.isPlainObject(array[i])) {
for (var property in array[i]) {
result[prefix + "[" + i + "]." + property] = array[i][property];
}
}
else {
result[prefix + "[" + i + "]"] = array[i];
}
}
}
</script>
What I have to do to send the properties of the lists schedules?
I had also looked at their serializeArray solution, but it didn't work for me in case of 3 level objects I had. I could have fixed that but then I didn't want to write recursive code. The solution I used is pretty straight-forward and aligned to the problem I had. Its very readable.
I absolutely wish Kendo should do this out of the box for their grid, but they told this when I raised a support question.
"You will need to send the values as additional data in this case because the built-in filtering does not support collection values. To format the data so that it will be bound by the model binder, you should follow the guidelines from my previous reply(dot notation for objects and indexer for arrays)"
Here is my C# ViewModels
//relates to one control value (for e.g. one entry in multi-select)
public class FormUnitFilter
{
public string Operator { get; set; }
public string Field { get; set; }
public string Value { get; set; }
public List<string> ValueList { get; set; }
}
//relates to a set of filters in a combined set (for e.g. the whole multi-select or a radiobutton or date control which appears in a single panel)
public class FormSetFilter
{
public List<FormUnitFilter> Filters { get; set; }
public string LogicalOperator { get; set; }
}
//relates to the whole set of filters present on the screen (for e.g. the filters across different panels)
public class FormWholeFilter
{
public List<FormSetFilter> Filters { get; set; }
public string LogicalOperator { get; set; }
}
here is my js function which converts this json model to a type recognized by MVC controller action parameter.
function buildFilterCriteria() {
var data = {};
if (modelObj) {
//reset the filters
modelObj.FormWholeFilter.Filters.length = 0;
//Assign FormWholeFilter data (outermost object)
data["FormWholeFilter.LogicalOperator"] = modelObj.FormWholeFilter.LogicalOperator;
//now iterate the filters inside FormWholeFilter (1st inner object)
for (var setIndex = 0; setIndex < modelObj.FormWholeFilter.Filters.length; setIndex++) {
var setFilter = modelObj.FormWholeFilter.Filters[setIndex];
data["FormWholeFilter.Filters[" + setIndex + "].LogicalOperator"] = setFilter.LogicalOperator;
//now iterate the filters inside FormSetFilter (2nd inner object)
for (var unitIndex = 0; unitIndex < setFilter.Filters.length; unitIndex++) {
var unitFilter = setFilter.Filters[unitIndex];
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Operator"] = unitFilter.Operator;
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Field"] = unitFilter.Field;
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Value"] = unitFilter.Value;
if (unitFilter.ValueList)
for (var valIndex = 0; valIndex < unitFilter.ValueList.length; valIndex++) {
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].ValueList[" + valIndex + "]"] = unitFilter.ValueList[valIndex];
}
}
}
}
return modelObj && data;
}
Here is my controller action method which takes the Kendo grid datasourcerequest and the FormWholeFilter I pass from JavaScript.
public JsonResult ProcessFilters([DataSourceRequest] DataSourceRequest request, FormWholeFilter formWholeFilter)
{
//Method body
}
Also, when I load the page for the first time, I had assigned the modelObj to the FormWholeFilter blank json like this and thats why I could use this variable in the buildFilterCriteria method:
var modelObj;
$(document).ready(function () {
modelObj = $.parseJSON('#Html.Raw(Json.Encode(#Model))');
});

Display Template For Generics - View is not found

I have the following classes:
public class Widget
{
public string Name { get; set; }
}
GenericModel
public class GenericModel<T>
{
public List<T> Data { get; set; }
}
My Controller action is:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View(model);
}
And my view is:
#model MyApp.GenericModel<MyApp.Widget>
#{
ViewBag.Title = "Simple";
}
<h2>Simple</h2>
#Html.DisplayFor(m=>m)
I have a file called GenericModel.cshtml in Views/Shared/DisplayTemplate folder:
#model MyApp.GenericModel<MyApp.Widget>
<ul>
#for (int i = 0; i < Model.Data.Count; i++ )
{
<li>
#Html.EditorFor(m=> Model.Data[i].Name)
</li>
}
</ul>
This view can not be found. I see when I print out the name of the type of my model I get "GenericModel1". Seeing that, I renamed my template "GenericModel1.cshtml". This seems like a bit of a hack, is there an easier way to find this display template without resorting to this?
You have to set it in your viewstart:
#Code
Layout = "~/Views/Shared/DisplayTemplate.cshtml"
End Code
Note: The above is VB.
You can also pass it via your controller like this:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View("", "DisplayTemplate", model);
}

Creating Canonical URLs including an id and title slug

I want to replicate what StackOverflow does with its URLs.
For example:
Hidden Features of C#? - (Hidden Features of C#?)
or
Hidden Features of C#? - (Hidden Features of C#?)
Will Take you to the same page but when they return to the browser the first one is always returned.
How do you implement the change so the larger URL is returned?
The way that I've handled this before is to have two routes, registered in this order
routes.MapRoute(
null,
"questions/{id}/{title}",
new { controller = "Questions", action = "Index" },
new { id = #"\d+", title = #"[\w\-]*" });
routes.MapRoute(
null,
"questions/{id}",
new { controller = "Questions", action = "Index" },
new { id = #"\d+" });
now in the controller action,
public class QuestionsController
{
private readonly IQuestionRepository _questionRepo;
public QuestionsController(IQuestionRepository questionRepo)
{
_questionRepo = questionRepo;
}
public ActionResult Index(int id, string title)
{
var question = _questionRepo.Get(id);
if (string.IsNullOrWhiteSpace(title) || title != question.Title.ToSlug())
{
return RedirectToAction("Index", new { id, title = question.Title.ToSlug() }).AsMovedPermanently();
}
return View(question);
}
}
We'll permanently redirect to the URL that contains the title slug (lowercase title with hyphens as separators) if we only have the id. We also make sure that the title passed is the correct one by checking it against the slugged version of the question title, thereby creating a canonical URL for the question that contains both the id and the correct title slug.
A couple of the helpers used
public static class PermanentRedirectionExtensions
{
public static PermanentRedirectToRouteResult AsMovedPermanently
(this RedirectToRouteResult redirection)
{
return new PermanentRedirectToRouteResult(redirection);
}
}
public class PermanentRedirectToRouteResult : ActionResult
{
public RedirectToRouteResult Redirection { get; private set; }
public PermanentRedirectToRouteResult(RedirectToRouteResult redirection)
{
this.Redirection = redirection;
}
public override void ExecuteResult(ControllerContext context)
{
// After setting up a normal redirection, switch it to a 301
Redirection.ExecuteResult(context);
context.HttpContext.Response.StatusCode = 301;
context.HttpContext.Response.Status = "301 Moved Permanently";
}
}
public static class StringExtensions
{
private static readonly Encoding Encoding = Encoding.GetEncoding("Cyrillic");
public static string RemoveAccent(this string value)
{
byte[] bytes = Encoding.GetBytes(value);
return Encoding.ASCII.GetString(bytes);
}
public static string ToSlug(this string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var str = value.RemoveAccent().ToLowerInvariant();
str = Regex.Replace(str, #"[^a-z0-9\s-]", "");
str = Regex.Replace(str, #"\s+", " ").Trim();
str = str.Substring(0, str.Length <= 200 ? str.Length : 200).Trim();
str = Regex.Replace(str, #"\s", "-");
str = Regex.Replace(str, #"-+", "-");
return str;
}
}

Resources