I am using MVC and want to pass values, controller to controller
My code:
public ActionResult Index()
{
List<string> SportsName = new List<string>();
var sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
var spt = Db.Departments.Where(i => i.UniversityID == sport.UniversityID && i.DepartmentCodeID == 4).SingleOrDefault();
unvId = int.Parse(sport.UniversityID.ToString());
List<Sport> dept = Db.Sports.Where(s => s.DepartmentID == spt.DepartmentID).ToList();
foreach (var sname in dept.ToList())
{
var name = Db.SportsCodes.Where(s => s.SportsCodeID == sname.SportsCodeID).First();
SportsName.Add(name.SportsName);
}
ViewBag.SportsName = SportsName;
return View();
}
public ActionResult Create(string sports)
{
ViewBag.SportsName = sports;
int s = unvId;
return View();
}
I want the 'sport' value in create action also. How to get the value of 'sport' in create action?
What I guess from your question is You want to pass the SportsName from Index to Create.
From Index View (.cshtml) when you call the Create method through AJAX call, pass the value of the ViewBag.Sports as a parameter.
For Example :
$('#Link').click(function () {
$.ajax({
url: http://localhost/Sports/Create,
type: 'GET',
data: {
sports: "#ViewBag.SportsName"
},
success: function () {
},
error: function () {
}
});
[Note : Here it is considered that name of your controller is Sports]
This answers to your question.
You have a number of options. If only individual action methods need that value, fetch the value in each action method that needs it:
public ActionResult SomeMethod()
{
var sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
// ...
}
If the repeated code seems unsightly, abstract it to a helper method:
public ActionResult SomeMethod()
{
var sport = GetSport();
// ...
}
private SomeType GetSport()
{
return Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
}
If it should be accessible anywhere in the class and is logically a class-level member, make it a class-level member:
private SomeType sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
public ActionResult SomeMethod()
{
// sport is accessible here
// ...
}
Though now that I notice it, this begs the question "What is Db"? If that's a database context then it looks like you've expanded the scope of it beyond what it really should be. Database contexts and connections should be kept in as small a scope as possible. Each method that needs one should create it, use it, and destroy it. Sharing them at a larger scope without explicitly knowing what you're doing is inviting a whole host of problems. In this case, the class-level member would be initialized in the constructor:
private SomeType sport;
public YourController()
{
using (var Db = BuildYourDBContext())
sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
}
public ActionResult SomeMethod()
{
// sport is accessible here
// ...
}
Any way you look at it, the point is that being a controller doesn't really make a difference. The controller is an object like any object in an object-oriented system. It shares members exactly the same way.
Related
documentation is very sparce and all i tried results in the deserializer injected but normal odata url's not working anymore.
https://github.com/OData/WebApi/issues/158 has solutions buut for 5.6.
The final relevant comment is:
#dbenzhuser - In that commit, look at ODataFormatterTests.cs for how
inject a custom deserializer/deserializer provider. You can still use
a custom DeserializerProvider but it's injected through DI instead of
injecting it through ODataMediaTypeFormatters.
which is quite meaningless. I tried the code there, but it breaks, as I said, the URL's.
Right now my Odata setup is simple:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddOData();
\UnitTest\Microsoft.AspNet.OData.Test.Shared\Formatter\ODataFormatterTests.cs
has examples to inject them (like in lines 379-383)
config.MapODataServiceRoute("IgnoredRouteName", null, builder =>
builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, sp => ODataTestUtil.GetEdmModel())
.AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new CustomSerializerProvider())
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", config)));
but I seem unable to get this working without removing the core odata routing.
Anyone an idea how to use that for the current version without breaking the base functionality?
There are three steps if you want to maintain the base functionality:
Your DeserializerProvider implementation should default to the base implementation for all scenarios that your custom Deserializer can't manage. In the following example the custom deserializer only operates on Resources and not Sets:
public class EntityTypeDeserializerProvider : DefaultODataDeserializerProvider
{
private readonly DataContractDeserializer _dataContractDeserializer;
public EntityTypeDeserializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
_dataContractDeserializer = new DataContractDeserializer(this);
}
public override ODataEdmTypeDeserializer GetEdmTypeDeserializer(IEdmTypeReference edmType)
{
if(edmType.Definition.TypeKind == EdmTypeKind.Complex || edmType.Definition.TypeKind == EdmTypeKind.Entity)
return _dataContractDeserializer;
else
return base.GetEdmTypeDeserializer(edmType);
}
}
As with the provider your custom _Deserializer should call through to the base implementation for everything that you do not need to customize. In the following implementation we are only trying to enforce the Order of the properties that are deserialized as well as calling the DataContract OnDeserializing and OnDeserialized methods, the rest of the deserialization is unaffected:
/// <summary>
/// OData serializer that oberys the DataMember Order Attribute and OnDeserializing and OnDeserialized attributes on the resource definition
/// </summary>
public class DataContractDeserializer : ODataResourceDeserializer
{
public DataContractDeserializer(ODataDeserializerProvider provider)
: base(provider) { }
public override object CreateResourceInstance(IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var resource = base.CreateResourceInstance(structuredType, readContext);
var type = resource.GetType();
if(type.GetCustomAttributesData().Any(x => x.AttributeType == typeof(DataContractAttribute)))
{
// manually call OnDeserializing
var init = type.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).FirstOrDefault(x => x.GetCustomAttributesData().Any(a => a.AttributeType == typeof(OnDeserializingAttribute)));
if(init != null)
{
init.Invoke(resource, new object[] { new StreamingContext(StreamingContextStates.Remoting, readContext ) });
}
}
return resource;
}
public override object ReadResource(ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var resource = base.ReadResource(resourceWrapper, structuredType, readContext);
var type = resource.GetType();
if (type.GetCustomAttributesData().Any(x => x.AttributeType == typeof(DataContractAttribute)))
{
// manually call OnDeserialized
var final = type.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).FirstOrDefault(x => x.GetCustomAttributesData().Any(a => a.AttributeType == typeof(OnDeserializedAttribute)));
if (final != null)
{
final.Invoke(resource, new object[] { new StreamingContext(StreamingContextStates.Remoting, readContext) });
}
}
return resource;
}
public override void ApplyStructuralProperties(object resource, ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var type = resource.GetType();
var memberDescriptors = type.GetProperties().Where(x => x.HasAttribute<DataMemberAttribute>());
if (memberDescriptors.Any())
{
var orderedProperties = from p in resourceWrapper.Resource.Properties
let clsProperty = memberDescriptors.FirstOrDefault(m => m.Name == p.Name)
let memberAtt = (DataMemberAttribute)(clsProperty?.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(DataMemberAttribute)))
orderby (memberAtt?.Order).GetValueOrDefault()
select p;
foreach (var property in orderedProperties)
{
ApplyStructuralProperty(resource, property, structuredType, readContext);
}
}
else
base.ApplyStructuralProperties(resource, resourceWrapper, structuredType, readContext);
}
}
Finally, You need to replace the default DeserializerProvider registration with your own, the following is an example of an overload to MapODataServiceRoute that registers the DeserializerProvider from the previous 2 examples.
I have commented out an example of registering a specific SerializerProvider
private static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
string routePrefix, IEdmModel model, ODataBatchHandler batchHandler = null, ODataUriResolver uriResolver = null, IList<IODataRoutingConvention> routingConventions = null)
{
return configuration.MapODataServiceRoute(routeName, routePrefix, builder =>
builder
.AddService(ServiceLifetime.Singleton, sp => model)
//.AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new DefaultODataSerializerProvider(sp))
.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new EntityTypeDeserializerProvider(sp))
.AddService(ServiceLifetime.Singleton, sp => batchHandler ?? new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer))
.AddService(ServiceLifetime.Singleton, sp => uriResolver ?? new ODataUriResolver())
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
routingConventions ??
ODataRoutingConventions.CreateDefaultWithAttributeRouting(routeName, configuration)
)
);
}
I have a MVC method like so:
public ActionResult ChangeStatus(string productId, string statusToChange)
{
var productToChangeStatus = _updateProductRepository.GetUpdateProduct(productId);
if (statusToChange.ToLower() == ChangeStatusTo.Disable)
{
productToChangeStatus.Active = "false";
}
else
{
productToChangeStatus.Active = "true";
}
_updateProductsManager.UpsertProduct(productToChangeStatus);
return Json(new { success = true });
}
This method gets an existing product based on the 'productId', changes the 'Active' property on it based on the 'statusToChange' value, saves it back and returns a Json with success.
The test setup is like so:
private ProductController _controller;
private Mock<IUpdateProductRepository> _iProductRepository;
[TestInitialize]
public void TestSetup()
{
_iProductRepository = new Mock<IUpdateProductRepository>();
_controller = new ProductController(_iProductRepository.Object);
}
Wrote a test method like so:
[TestMethod]
public void Disable_A_Product_Which_Is_Currently_Enabled()
{
const string productId = "123";
var productBeforeStatusChange = new Product()
{
Active = "true",
Id = new Guid().ToString(),
Name = "TestProduct",
ProductId = "123"
};
var productAfterStatusChange = new Product()
{
Active = "false",
Id = new Guid().ToString(),
Name = "TestProduct",
ProductId = "123"
};
_iProductRepository.Setup(r => r.GetUpdateProduct(productId)).Returns(productBeforeStatusChange);
_iProductRepository.Setup(r => r.UpsertProduct(productBeforeStatusChange)).Returns(productAfterStatusChange);
var res = _controller.ChangeStatus("123", "disable") as JsonResult;
Assert.AreEqual("{ success = true }", res.Data.ToString());
}
The test fails with this error:
Object reference not set to an instant of the object.
On debugging I found that it fails inside the
if(...)
condition where the actual setting of the Active property is happening.
Since the productId that's being passed is not real a product object can't be retrieved for the code to work on.
I tried to use Mock but I think my usage is not correct.
So what I want to know is, how to test a method like this where a method that returns an ActionResult is in turn calling the repository to work with object(s).
Thanks in advance.
You seems to be missing setup for
_updateProductsManager.UpsertProduct()
The way you setup GetUpdateProduct() method you ought to setup UpsertProduct() on the mock instance.
I am new for unit test with Moq and Nunit. I have a simple unit test for getting a product list. But the test result didn't turn out as expected.
ProductManagementController action is:
public ViewResult Index(int? id)
{
return View(_ProductRepo.GetProductList(id));
}
ProductRepository is:
public IList<Product> GetProductList(int? id)
{
if (id == null)
{
return (db.Products).ToList();
}
else
{
return db.Products.Where(i => i.CategoryId == id).ToList();
}
}
I have two tests setup, but only the first test(get all products list) is fine.
[TestFixture]
public class Test_ProductManagement
{
private List<Product> products;
private List<Category> categories;
private Mock<IProductRepository> mockProducts;
private ProductManagementController target;
[TestFixtureSetUp]
public void Initialize()
{
images = new List<Product> {
new Product{Id = 1, Name = "Boat", CategoryId = 1 },
new Product{Id = 2, Name = "Picture Frame",CategoryId = 2 }
};
categories = new List<Category> {
new Category { Id = 1, Name = "Outdoors" },
new Category { Id = 2, Name = "Housewares" }
};
mockProducts = new Mock<IProductRepository>();
mockProducts.Setup(m => m.GetProductList(It.IsAny<int>())).Returns(products);
target = new ProductManagementController(mockProducts.Object);
}
[Test]
public void Index_Contains_All_Products()
{
//Action
List<Product> result = ((IEnumerable<Product>)target.Index(It.IsAny<int>()).ViewData.Model).ToList();
//Assert
Assert.AreEqual(2, result.Length);
Assert.AreEqual("Boat", result[0].Name);
Assert.AreEqual("Picture Frame", result[1].Name);
}
[Test]
public void Index_Get_ImageList_By_CategoryId()
{ //Arrange
int id = 2;
//Action
List<Product> result = ((IEnumerable<Product>)target.Index(id).ViewData.Model).ToList();
//Assert
Assert.AreEqual(1, result.Count());
Assert.AreEqual("Picture Frame", result[0].Name);
}
The 2nd test always return a full product list which include 2 products while I only expect 1 returned from Assert.AreEqual(1,result.Count());
I can't figure out why test code always take the id as a null parameter in the above index call: target.Index(id) . However all my project codes run correctly in the browsers.
Does anyone has a clue? Thanks.
I can't figure out why test code always take the id as a null parameter in the above index call: target.Index(id)
It doesn't. You don't show all code (specifically how your controller handles the repository injection) and you seem to have renamed at least the products / images field, but the code works just fine.
You call target.Index(id), which in turn calls _ProductRepo.GetProductList(id). I assume this is your mock being called, which is Setup() by you to always return the entire product list.
The mock does exactly what you ask it to, no ProductRepository is used in this code, so your if (id == null) is never executed.
In order to fix this, you must Setup() your mock differently in each test method, or setup both cases in advance:
mockProducts.Setup(m => m.GetProductList(null)).Returns(products);
mockProducts.Setup(m => m.GetProductList(2)).Returns(new List<Product>{products[1]});
Of course you want to setup the latter call somewhat less specific, but I hope you see where you can fix this.
I have ASP.NET MVC 4 application with one view model class and about 20 views representing this view model. This views differs only by fields which user can edit. I want to merge all that views to one and define list of properties available to editing in strongly-typed manner. Ideally, I want something like this:
// Action
public ActionResult EditAsEngineer(int id)
{
//...
viewModel.PropertiesToChange = new List<???>()
{
v => v.LotNumber,
v => v.ShippingDate,
v => v.Commentary
};
return View(viewModel);
}
// View
if (#Model.PropertiesToChange.Contains(v => v.LotNumber)
{
#Html.TextBoxFor(m => m.LotNumber)
}
else
{
#Model.LotNumber
}
Is it possible to do something like this? Or is there a better solution?
Thank you.
Why note something like this (its pseudo code)
public class Prop{
string PropertyName {get;set;}
bool PropertyEditable {get;set;}
}
public ActionResult EditAsEngineer(int id)
{
viewModel.PropertiesToChange = new List<Prop>()
{
new Prop{PropertyName = LotNumber, PropertyEditable = true}
};
return View(viewModel);
}
#foreach (var pin Model.PropertiesToChange)
{
if(p.PropertyEditable){
#Html.TextBoxFor(p)
}else{
#Html.DisplayFor(p)
}
}
This will solve HALF of your problem. You will also need to create a IEqualityComparer<Expression> for your code to work (the default is to check for ref-equals).
return from p in typeof(T).GetProperties()
let param = System.Linq.Expressions.Expression.Parameter(typeof(T), "x")
let propExp = System.Linq.Expressions.Expression.Property(param, p)
let cast = System.Linq.Expressions.Expression.Convert(propExp, typeof(object))
let displayAttribute = p.CustomAttributes.OfType<System.ComponentModel.DataAnnotations.DisplayAttribute>()
.Select(x => x.Order).DefaultIfEmpty(int.MaxValue).FirstOrDefault()
orderby displayAttribute
select System.Linq.Expressions.Expression.Lambda<Func<T, object>>(cast, new [] {param});
This will list out ALL the properties for T. You would also probabily want to use Expression<Func<T, object>> as the type for defining your list of properties.
This will allow you to create a generic view over all properties.
Also you will want to wrap this in some kind of a cache, as this code is SLOW.
It is understood that:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.Equals(second)); // Output is "False"
However, examining the strings of each expression does show equality:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.ToString().Equals(second.ToString())); // Output is "True"
This idea was a culmination of different posts...
http://www.codethinked.com/Comparing-Simple-Lambda-Expressions-With-Moq
Moq'ing methods where Expression<Func<T, bool>> are passed in as parameters
Verify method was called with certain linq expression (moq)
The intent:
I am writing an MVC application using the repository pattern such that
public class MyController : Controller
{
public Repository.IRepository Repository { get; set; }
public MyController()
{
this.Repository = new Repository.CommonRepository();
}
public MyController(Repository.IRepository repository)
{
this.Repository = repository;
}
[HttpPost]
public ActionResult Create(Domain.Common.Object1 o1)
{
if (ModelState.IsValid)
{
// Additional validation
o1.Name = o1.Name.Trim();
if (this.Repository.Any<Domain.Common.Object1>(a => a.Name.ToLower() == plant.Name.ToLower()))
this.ModelState.AddModelError("Name", "Duplicate found.");
}
if (ModelState.IsValid)
{
var entity = this.Repository.Add(o1);
if (Request.IsAjaxRequest())
return this.Json(new { Completed = true, Id = entity.Id });
return RedirectToAction("Details", new { id = entity.Id });
}
if (Request.IsAjaxRequest())
return PartialView("_Create", o1);
return View("Create", o1);
}
}
Repository is a completely separate project as is the domain. My repository code is setup so that I can use the one repository to query any object based upon the generic:
public IQueryable<T> GetAll<T>() where T : AbstractEntity
{
return this.DbContext.Set<T>();
}
Note: AbstractEntity is a domain abstract class all of my POCO objects inherit from.
Everything is fine when using Moq to unit test the controller :
[TestMethod]
public void Create_Post_DuplicateNameAddsError()
{
// Arrange
var repository = new Mock<Repository.IRepository>();
repository.Setup(a => a.Any<Domain.Common.Object1>(It.IsAny<System.Linq.Expressions.Expression<Func<Domain.Common.Object1, bool>>>()))
.Returns(true);
var controller = ControllerFactory<MyController>.GetController();
controller.Repository = repository.Object;
var model = new Domain.Common.Object1()
{
Id = Guid.NewGuid()
,
Name = "Name"
};
// Act
var result = controller.Create(model) as ViewResult;
// Assert
Assert.IsFalse(controller.ModelState.IsValid);
Assert.IsNotNull(result);
Assert.AreEqual("Create", result.ViewName, false);
Assert.AreEqual(model, result.Model);
}
Note: ControllerFactory is a way to generate a controller with certain properties filled, such as Request, Response, User, Request.Headers ect...
Where this fails is if I have to use IRepository.Any(predicate) more than once, or any method that uses expressions that is called more than once. I need it to say true for one and false for another. If the expression strings were a match, this would be a non-issue, but as everything is in different projects the expression strings come out as:
a => (a.Name.ToLower() == value(foo.Web.Tests.Controllers.Object1ControllerTests+<>c__DisplayClass3).ob1.Name.ToLower())
a => (a.Name.ToLower() == value(foo.Controllers.MyController+<>c__DisplayClass1).ob1.Name.ToLower())
The difference lies in the value function. I have tried matching from Regular Expressions, which works, but is ugly as you have to escape every .<>(), which in turn makes it very difficult to maintain.
I tried using Matt Meber's Expression Equality Comparer, but they are not equal due to that value function (my belief).
Suggestions?