Why am I getting a exception when ApplyPropertyChanges???
The code is almost the same when I'm editing a user table but is not working with my news table.
The create, delete and details are all working fine but when I try to edit a news I'm getting the exception below:
The ObjectStateManager does not contain a ObjectStateEntry 'MagixCMS.Models.noticia'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MagixCMS.Models
{
public class NoticiaRepository : INoticiaRepository
{
#region INoticiaRepository Members
magixcmsEntities _entities = new magixcmsEntities();
public noticia CreateNoticia(noticia noticiaToCreate)
{
_entities.AddTonoticiaSet(noticiaToCreate);
_entities.SaveChanges();
return noticiaToCreate;
}
public void DeletaNoticia(noticia noticiaToDelete)
{
var noticiaOriginal = GetNoticia(noticiaToDelete.Id);
_entities.DeleteObject(noticiaOriginal);
_entities.SaveChanges();
}
public noticia EditNoticia(noticia noticiaToEdit)
{
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
_entities.ApplyPropertyChanges(noticiaToEdit.EntityKey.EntitySetName, noticiaToEdit); //EXCEPTION HERE
_entities.SaveChanges();
return noticiaToEdit;
}
public noticia GetNoticia(int id)
{
return (from c in _entities.noticiaSet where c.Id == id select c).FirstOrDefault();
}
public IEnumerable<noticia> ListNoticias()
{
return _entities.noticiaSet.ToList();
}
#endregion
}
}
I google the exception and didn't found much help.
I solve it.
The problem is on the EF model.
To solve it you'll need a extension method to persist your data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace MagixCMS.Models
{
public static class Extensions
{
public static void Update(ObjectContext context, string entitySetName, IEntityWithKey entity)
{
entity.EntityKey = context.CreateEntityKey(entitySetName, entity);
context.Attach(entity);
var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (var propName in propertyNameList)
{
stateEntry.SetModifiedProperty(propName);
}
}
}
}
And in the Edit method you do:
public noticia EditNoticia(noticia noticiaToEdit)
{
//GET THE CONTEXT FOR THE ENTITY
ObjectContext _context = this._entities.noticiaSet.Context;
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
//UPDATE THE ORIGINAL ENTITY WITH THE NEW VALUES
Extensions.Update(_context, noticiaOriginal.EntityKey.EntitySetName, noticiaToEdit);
//PERSIST THE DATA
_entities.SaveChanges();
return noticiaToEdit;
}
Related
I am building a Web API in ASP.NET Core, I am using stored procedures to be able to handle more complex queries, which with the Entity Framework is too complicated for me, I am using ADO.NET to make this connection.
I have managed to connect to a stored procedure and use the get and post methods, the point is that I don't know how to do it in order to call the other stored procedures and map a route to interact via get or post in the same project. I have only been able to do one, and I don't think it would be more convenient to create a Web API for each function that complies with a stored procedure.
My project is made up of three folders called Controller, Data, Models.
Within Models is the Value class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Models
{
public class Value
{
public int Id { get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
}
}
Data folder has a class called ValueRepository
using ASPNETCore_StoredProcs.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Data
{
public class ValuesRepository
{
private readonly string _connectionString;
public ValuesRepository(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("defaultConnection");
}
public async Task<List<Value>> GetAll()
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAllValues", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var response = new List<Value>();
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response.Add(MapToValue(reader));
}
}
return response;
}
}
}
private Value MapToValue(SqlDataReader reader)
{
return new Value()
{
Id = (int)reader["Id"],
Value1 = (int)reader["Value1"],
Value2 = reader["Value2"].ToString()
};
}
public async Task<Value> GetById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetValueById", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
Value response = null;
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response = MapToValue(reader);
}
}
return response;
}
}
}
public async Task Insert(Value value)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#value1", value.Value1));
cmd.Parameters.Add(new SqlParameter("#value2", value.Value2));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
public async Task DeleteById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("DeleteValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
}
}
and finally I have a controller class called ValuesController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using ASPNETCore_StoredProcs.Models;
using Microsoft.AspNetCore.Mvc;
namespace ASPNETCore_StoredProcs.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ValuesRepository _repository;
public ValuesController(ValuesRepository repository)
{
this._repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
// GET api/values
[HttpGet]
public async Task<ActionResult<IEnumerable<Value>>> Get()
{
return await _repository.GetAll();
}
// GET api/values/5
[HttpGet("{id}")]
public async Task<ActionResult<Value>> Get(int id)
{
var response = await _repository.GetById(id);
if (response == null) { return NotFound(); }
return response;
}
// POST api/values
[HttpPost]
public async Task Post([FromBody] Value value)
{
await _repository.Insert(value);
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public async Task Delete(int id)
{
await _repository.DeleteById(id);
}
}
}
This is my startup class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ASPNETCore_StoredProcs
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ValuesRepository>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
I appreciate if you can help me or if it is how I think I should create an API for each procedure that is performed thanks
When I debug my project, and add an item to cart I am thrown this error, not sure what to do?
System.InvalidCastException: 'Unable to cast object of type
'System.Collections.Generic.List1[ShoppingCartApp.Classes.CartItem]'
to type
'System.Collections.Generic.List1[ShoppingCartApp.Controllers.CartItem]'.'
The error is thrown on this line #foreach (CartItem cartItem in (List<CartItem>)Session["shoppingCart"]) in my view
Heres my controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ShoppingCartApp.ServiceReference1;
using ShoppingCartApp.Classes;
public class ShoppingCartController : Controller
{
Service1Client wcf = new Service1Client(); // Connection to WCF
// GET: ShoppingCart
public ActionResult ShoppingCart()
{
return View("ShoppingCart");
}
private int checkIfExisting(int id) // Checks session for existing products, increases quantity if product exists
{
List<CartItem> shoppingCart = (List<CartItem>)Session["shoppingCart"];
for (int x = 0; x < shoppingCart.Count; x++)
if (shoppingCart[x].Product.ProductID == id)
return x;
return -1;
}
public ActionResult RemoveFromCart(int id)
{
List<CartItem> shoppingCart = new List<CartItem>();
shoppingCart.Remove(new CartItem(wcf.GetProduct(id), 1));
Session["shoppingCart"] = shoppingCart;
return View("ShoppingCart");
}
public ActionResult AddToCart(int id) // Creates new session if one is not existing, adds item to cart, uses checkifexisting method to increase quantity
{
if(Session["shoppingCart"] == null)
{
List<CartItem> shoppingCart = new List<CartItem>();
shoppingCart.Add(new CartItem(wcf.GetProduct(id), 1));
Session["shoppingCart"] = shoppingCart;
} else
{
List<CartItem> shoppingCart = (List <CartItem>)Session["shoppingCart"];
int index = checkIfExisting(id);
if (index == -1)
shoppingCart.Add(new CartItem(wcf.GetProduct(id), 1));
else
shoppingCart[index].Quantity++;
Session["shoppingCart"] = shoppingCart;
}
return View("ShoppingCart");
}
public ActionResult Checkout()
{
OrderDetail orderDetail = wcf.GetOrderDetail();
return View();
}
}
And heres the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ShoppingCartApp.ServiceReference1;
namespace ShoppingCartApp.Classes
{
public class CartItem
{
private Product product = new Product();
public Product Product
{
get { return product; }
set { product = value; }
}
private int quantity;
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
public CartItem(Product product, int quantity)
{
this.product = product;
this.Quantity = quantity;
}
}
}
What exactly is it telling me to do?
in my view I changed ShoppingCartApp.Controllers to ShoppingCartApp.Classes, which resolved the issue completely
My Seed() method is getting rather long-running. Is there a way to write to the console whilst running the update-database command?
So far, Console.WriteLine() and Debug.WriteLine() have not written output to the Package Manager Console.
You can avoid run-on seed methods by extending the base DbContext class with a method that accepts an interface and returns the context instance to keep things fluent.
Though it doesn't solve the issue of not being able to write console/debug output in the package manager console, it will tell you which entity that you failed on while keeping things tidy.
Configuration.cs
using System.Configuration;
using MySolution.Data.Migrations.Seedings;
namespace MySolution.Data.Migrations
{
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<MySolution.Data.MySolutionContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(MySolution.Data.MySolutionContext context)
{
context
.AppendSeeding<AddressSeeding>(true)
.AppendSeeding<CustomerSeeding>(true)
.AppendSeeding<OrderSeeding>(true)
.AppendSeeding<ShipmentSeeding>(true)
.AppendSeeding<ApplicationSettingsSeeding>();
}
}
}
DbContextExtensions.cs
using System;
using System.Data.Entity;
namespace MySolution.Data.Migrations {
public static class DbContextExtensions {
public static DbContext AppendSeeding<T>(
this DbContext context, bool saveChanges = false)
where T : ISeeding, new() {
var seeding = new T();
try {
seeding.Seed(context);
if (saveChanges) {
context.SaveChanges();
}
}
catch (Exception ex) {
throw new Exception(
$"Unhandled exception while appending seed data via {seeding.GetType().Name}",
ex);
}
return context;
}
}
}
ISeeding.cs
using System.Data.Entity;
namespace MySolution.Data.Migrations {
public interface ISeeding {
void Seed(DbContext context);
}
}
AddressSeeding.cs (Example implementation of ISeeding)
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using MySolution.Data.Models;
namespace MySolution.Data.Migrations.Seedings {
public class AddressSeeding : ISeeding {
public void Seed(DbContext context) {
((MySolutionContext)context).Address.AddOrUpdate(
address => new { address.Address1},
new Address {
Address1 = "111 Fake St.",
City = "Nowhereland",
County = "Countyville",
State = "ST",
Zip = "12345",
AddressType = new AddressType {
Name = "Home"
},
}
);
}
}
}
Note: I can't bountify this question yet (it's too new), but I will reward a good answer with 50 points, and a great answer with 100 (when possible).
I need to incorporate DI into my Web API project. I currently have the expected Model and Controller folders/classes, along with corresponding Repository classes.
That seemed to work well for awhile, but now I need to use DI with the Controllers so that I can pass an Interface type to the Controllers' constructor.
I'm struggling with just how to implement this; that is, how to incorporate the DI "extravaganza" into my existing Model/Controller/Repository structure. I have example DI code, but I don't know just how it should be applied to my project.
Perhaps some code is in order to try to make this clear. I will show a simple sample of what I've got, followed by the DI code I'd like to somehow incorporate into it / with it.
Here is the existing Model/Controller/Repository code:
MODEL
public class Department
{
public int Id { get; set; }
public int AccountId { get; set; }
public string Name { get; set; }
}
CONTROLLER
public class DepartmentsController : ApiController
{
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
{
return _deptsRepository.Get(ID, CountToFetch);
}
public void PostDepartment(int accountid, string name)
{
_deptsRepository.PostDepartment(accountid, name);
}
public HttpResponseMessage Post(Department department)
{
// Based on code 2/3 down http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC4-and-WebAP?msg=4727042#xx4727042xx
department = _deptsRepository.Add(department);
var response = Request.CreateResponse<Department>(HttpStatusCode.Created, department);
string uri = Url.Route(null, new { id = department.Id });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
REPOSITORY
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;User ID=BlaBlaBla...
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT td_department_accounts.dept_no,
IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name
FROM t_accounts INNER JOIN td_department_accounts ON
t_accounts.account_no = td_department_accounts.account_no ORDER BY
td_department_accounts.dept_no";
cmd.CommandType = CommandType.Text;
conn.Open();
int i = 1;
using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
{
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
int deptNum = oleDbD8aReader.GetInt16(0);
string deptName = oleDbD8aReader.GetString(1);
Add(new Department { Id = i, AccountId = deptNum, Name,
deptName });
i++;
}
}
}
}
}
public int Get()
{
return departments.Count;
}
private Department Get(int ID) // called by Delete()
{
return departments.First(d => d.Id == ID);
}
public IEnumerable<Department> Get(int ID, int CountToFetch)
{
return departments.Where(i => i.Id > ID).Take(CountToFetch);
}
public Department Add(Department dept)
{
if (dept == null)
{
throw new ArgumentNullException("Department arg was null");
}
// This is called internally, so need to disregard Id vals that already exist
if (dept.Id <= 0)
{
int maxId = departments.Max(d => d.Id);
dept.Id = maxId + 1;
}
if (departments != null) departments.Add(dept);
return dept;
}
public void PostDepartment(int accountid, string name)
{
int maxId = departments.Max(d => d.Id);
Department dept = new Department();
dept.Id = maxId + 1;
dept.AccountId = accountid;
dept.Name = name;
departments.Add(dept);
}
public void Post(Department department)
{
int maxId = departments.Max(d => d.Id);
department.Id = maxId + 1;
departments.Add(department);
}
public void Put(Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == department.Id);
departments[index] = department;
}
public void Put(int id, Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == id);
departments[index] = department;
}
public void Delete(int id)
{
Department dept = Get(id);
departments.Remove(dept);
}
And now here is the DI code that I want to incorporate
Classes in the DIInstallers folder:
IDepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public interface IDepartmentProvider
{
// These are the methods that are in the sample example IAuthProvider interface; I don't know what I need yet, though...
//bool Authenticate(string username, string password, bool createPersistentCookie);
//void SignOut();
}
}
DepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public class DepartmentProvider : IDepartmentProvider
{
// TODO: Implement methods in IDepartmentProvider, once they have been added
}
}
DepartmentProviderInstaller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace HandheldServer.DIInstallers
{
public class DepartmentProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(IDepartmentProvider))
.WithServiceAllInterfaces());
// If I declare/implement more interface types (other than IDepartmentProvider), I assume there would be another container.Register() call for each of them?
}
}
}
Classes in the DIPlumbing folder:
WindsorCompositionRoot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.Windsor;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace HandheldServer.DIPlumbing
{
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
}
WindsorControllerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace HandheldServer.DIPlumbing
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
}
The Global.asax.cs file
using System;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Castle.Windsor;
using Castle.Windsor.Installer;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Http.Dispatcher;
using HandheldServer.DIPlumbing;
namespace HandheldServer
{
public class WebApiApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
BootstrapContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
// Code that runs when an unhandled error occurs
void Application_Error(object sender, EventArgs e)
{
// Get the exception object.
Exception exc = Server.GetLastError();
log.Error(exc.Message);
// Clear the error from the server
Server.ClearError();
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
}
protected void Application_End()
{
container.Dispose();
}
}
}
So, I think I've basically got the code I need, but how to fold the DI code into my previous (Model/Controller/Repository) code is the part that has me stumped.
You can simply use WebApiContrib.IoC.CastleWindsor (Nuget).
This test should give you an idea of how to use it.
I've been trying to implement David Hayden's Orchard CMS and ASP .NET Web API http://www.davidhayden.me/blog/orchard-cms-and-asp.net-web-api, but I'm running into problems, basically getting a page not found.
This is what I have:
Under my controllers
ContactViewModel.cs
namespace Sunkist.ContactManager.Controllers
{
public class ContactsController : ApiController
{
private readonly IContentManager _contentManager;
public ContactsController(IContentManager contentManager)
{
_contentManager = contentManager;
}
public IEnumerable<ContactViewModel> Get()
{
return _contentManager
.Query(VersionOptions.Published, "Contact")
.List()
.Select(c => new ContactViewModel(c));
}
public ContactViewModel Get(int id)
{
var contact = _contentManager.Get(id);
if (contact == null)
throw new HttpResponseException
(new HttpResponseMessage(HttpStatusCode.NotFound));
return new ContactViewModel(contact);
}
}
}
ViewModel folder
ViewModel.cs
namespace Sunkist.ContactManager.ViewModel
{
public class ContactViewModel
{
private Orchard.ContentManagement.ContentItem c;
public ContactViewModel(Orchard.ContentManagement.ContentItem c)
{
// TODO: Complete member initialization
this.c = c;
}
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
}
}
and migration
namespace Sunkist.ContactManager {
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table ContactRecord
SchemaBuilder.CreateTable("ContactRecord", table => table
.ContentPartRecord()
.Column("Name", DbType.String)
.Column("Address", DbType.String)
.Column("City", DbType.String)
);
return 1;
}
}
}
I'm new to both Orchard and .Net MVC, So I'm not sure what I'm doing wrong?
Double check the name of the module that contains the Web API controller.
Via VS GUI, I created a new web api controller class "TestController.cs" and could not find the correct endpoint url.
That is, until I looked at the Module.txt in the project I added the controller to. The project is "Orchard.Users", but the name in the Module.txt file is just "Users".
I was able to hit the endpoint at "http://example.com/api/users/test".
Noteworthy: I setup a route for this at ".../UsersApi", but the 'auto-magic' URL pattern still works. Code below. Also, I later added a new module to contain my api controller and it would not work until I enabled the module in the dashboard.
TestController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Orchard.Users.Controllers
{
public class TestController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
HttpRoutes.cs
using System.Collections.Generic;
using Orchard.Mvc.Routes;
using Orchard.WebApi.Routes;
public class HttpRoutes : IHttpRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
foreach (RouteDescriptor routeDescriptor in GetRoutes())
{
routes.Add(routeDescriptor);
}
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new HttpRouteDescriptor {
Name = "UsersApi",
Priority = -10,
RouteTemplate = "usersapi/{id}",
Defaults = new {
area = "Orchard.Users",
controller = "Test",
id = RouteParameter.Optional
},
}
};
}
}