Create map with DBContext lookup, using automapper and ninject - c#

So what I'm trying to do is use my DBContext to get some information from the DB for a mapping.
So I create a custom TypeConverter:
public class RoundVMtoTrampetRound : ITypeConverter<RoundVM, TrampetRound>
{
public RoundVMtoTrampetRound(DBTariff context)
{
this.context = context;
}
private DBTariff context { get; set; }
public TrampetRound Convert(ResolutionContext context)
{
RoundVM source = (RoundVM)context.SourceValue;
Mapper.CreateMap<RoundVM, TrampetRound>();
var dest = Mapper.Map<TrampetRound>(source);
dest.Difficulty = this.context.DifficultyTrampet.Find(source.Id);
return dest;
}
}
And in my controller i create a mapper using:
Mapper.CreateMap<RoundVM, TrampetRound>().ConvertUsing<RoundVMtoTrampetRound>();
But when i do the mapping i get error message saying that there is no default constructor. But i want ninject to do this for me with the code:
kernel.Bind<DBTariff>().ToSelf().InRequestScope();
An answer is here but i still get the same problem
Automapper + EF4 + ASP.NET MVC - getting 'context disposed' error (I know why, but how to fix it?)
I have tried the solution given in the link but get the same error.
So how to i get Automapper to use my Ninject to fix the problem?
Edit
I also found this where the same thing is done with autofac
http://thoai-nguyen.blogspot.se/2011/10/autofac-automapper-custom-converter-di.html
so my guess is that i need to tell automapper to use my ninject resolver but how to i do that and where?

All information you need is given in the blog article you already linked.
To break it down into the absolute minimum information, you need to do:
Mapper.Initialize(x =>
{
x.ConstructServicesUsing(type => kernel.Get(type));
});
before you ever access any other Mapper. property / method, so you need to call Mapper.Initialize(..) before you do Mapper.CreateMap.

Related

Returning a List or IEnumerable with IHttpActionResult [duplicate]

I am working with ASP.NET MVC 5 Web Api. I want consult all my users.
I wrote api/users and I receive this:
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'"
In WebApiConfig, already I added these lines:
HttpConfiguration config = new HttpConfiguration();
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
But it still doesn't work.
My function for return data is this:
public IEnumerable<User> GetAll()
{
using (Database db = new Database())
{
return db.Users.ToList();
}
}
If you are working with EF, besides adding the code below on Global.asax
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Dont`t forget to import
using System.Data.Entity;
Then you can return your own EF Models
Simple as that!
When it comes to returning data back to the consumer from Web Api (or any other web service for that matter), I highly recommend not passing back entities that come from a database. It is much more reliable and maintainable to use Models in which you have control of what the data looks like and not the database. That way you don't have to mess around with the formatters so much in the WebApiConfig. You can just create a UserModel that has child Models as properties and get rid of the reference loops in the return objects. That makes the serializer much happier.
Also, it isn't necessary to remove formatters or supported media types typically if you are just specifying the "Accepts" header in the request. Playing around with that stuff can sometimes make things more confusing.
Example:
public class UserModel {
public string Name {get;set;}
public string Age {get;set;}
// Other properties here that do not reference another UserModel class.
}
Given right answer is one way to go, however it is an overkill when you can fix it by one config settings.
Better to use it in the dbcontext constructor
public DbContext() // dbcontext constructor
: base("name=ConnectionStringNameFromWebConfig")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'
Add this code to global.asax below on Application_Start:
Update from .Ignore to .Serialize . It must work.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
public class UserController : ApiController
{
Database db = new Database();
// construction
public UserController()
{
// Add the following code
// problem will be solved
db.Configuration.ProxyCreationEnabled = false;
}
public IEnumerable<User> GetAll()
{
return db.Users.ToList();
}
}
I resolved it using this code to WebApiConfig.cs file
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
I don't like this code:
foreach(var user in db.Users)
As an alternative, one might do something like this, which worked for me:
var listOfUsers = db.Users.Select(r => new UserModel
{
userModel.FirstName = r.FirstName;
userModel.LastName = r.LastName;
});
return listOfUsers.ToList();
However, I ended up using Lucas Roselli's solution.
Update: Simplified by returning an anonymous object:
var listOfUsers = db.Users.Select(r => new
{
FirstName = r.FirstName;
LastName = r.LastName;
});
return listOfUsers.ToList();
Adding this in your Application_Start() method of Global.asax file should solve the problem
protected void Application_Start()
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
// ...
}
METHOD 2: [Not recommended]
If you are working with EntityFramework, you can disable proxy in your DbContext class constructor. NOTE: this code wll be removed if you update the model
public class MyDbContext : DbContext
{
public MyDbContext()
{
this.Configuration.ProxyCreationEnabled = false;
}
}
There's also this scenario that generate same error:
In case of the return being a List<dynamic> to web api method
Example:
public HttpResponseMessage Get()
{
var item = new List<dynamic> { new TestClass { Name = "Ale", Age = 30 } };
return Request.CreateResponse(HttpStatusCode.OK, item);
}
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
So, for this scenario use the [KnownTypeAttribute] in the return class (all of them) like this:
[KnownTypeAttribute(typeof(TestClass))]
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
This works for me!
My personal favorite: Just add the code below to App_Start/WebApiConfig.cs. This will return json instead of XML by default and also prevent the error you had. No need to edit Global.asax to remove XmlFormatter etc.
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Just put following lines in global.asax:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Import
using System.Data.Entity;
Use AutoMapper...
public IEnumerable<User> GetAll()
{
using (Database db = new Database())
{
var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
return users;
}
}
Use the following namespace:
using System.Web.OData;
Instead of :
using System.Web.Http.OData;
It worked for me
Add the below line
this.Configuration.ProxyCreationEnabled = false;
Two way to use ProxyCreationEnabled as false.
Add it inside of DBContext Constructor
public ProductEntities() : base("name=ProductEntities")
{
this.Configuration.ProxyCreationEnabled = false;
}
OR
Add the line inside of Get method
public IEnumerable<Brand_Details> Get()
{
using (ProductEntities obj = new ProductEntities())
{
this.Configuration.ProxyCreationEnabled = false;
return obj.Brand_Details.ToList();
}
}
Use [Serializable] for class:
Example:
[Serializable]
public class UserModel {
public string Name {get;set;}
public string Age {get;set;}
}
It worked for me!
Solution that worked for me:
Use [DataContract] for class and [DataMember] attributes for each property to serialize. This is enough to get Json result (for ex. from fiddler).
To get xml serialization write in Global.asax this code:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
Read this article, it helped me to understand serialization:
https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
To add to jensendp's answer:
I would pass the entity to a user created model and use the values from that entity to set the values in your newly created model. For example:
public class UserInformation {
public string Name { get; set; }
public int Age { get; set; }
public UserInformation(UserEntity user) {
this.Name = user.name;
this.Age = user.age;
}
}
Then change your return type to: IEnumerable<UserInformation>
While all these answers above are correct, one may want to check the InnerException > ExceptionMessage.
If it says something like this "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.". This could be an issue because of default behavior of the EF.
By assigning LazyLoadingEnabled = false in your DbContext constructor will do the trick.
public class MyDbContext : DbContext
{
public MyDbContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
For more detailed reading about EagerLoading and LazyLoading behavior of EF refer this MSDN Article.
In my case I have had similar error message:
The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/xml; charset=utf-8'.
But when I dig deeper in it, the issue was:
Type 'name.SomeSubRootType'
with data contract name
'SomeSubRootType://schemas.datacontract.org/2004/07/WhatEverService'
is not expected. Consider using a DataContractResolver if you are
using DataContractSerializer or add any types not known statically to
the list of known types - for example, by using the KnownTypeAttribute
attribute or by adding them to the list of known types passed to the
serializer.
The way I solved by adding KnownType.
[KnownType(typeof(SomeSubRootType))]
public partial class SomeRootStructureType
This was solved inspired from this answer.
Reference: https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx
I basically add one line which they are
entities.Configuration.ProxyCreationEnabled = false;
to UsersController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using UserDataAccess;
namespace SBPMS.Controllers
{
public class UsersController : ApiController
{
public IEnumerable<User> Get() {
using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
entities.Configuration.ProxyCreationEnabled = false;
return entities.Users.ToList();
}
}
public User Get(int id) {
using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
entities.Configuration.ProxyCreationEnabled = false;
return entities.Users.FirstOrDefault(e => e.user_ID == id);
}
}
}
}
You will have to define Serializer Formatter within WebApiConfig.cs available in App_Start Folder like
Adding config.Formatters.Remove(config.Formatters.XmlFormatter);
// which will provide you data in JSON Format
Adding config.Formatters.Remove(config.Formatters.JsonFormatter);
// which will provide you data in XML Format
Another case where I received this error was when my database query returned a null value but my user/view model type was set as non-nullable. For example, changing my UserModel field from int to int? resolved.
This also happens when the Response-Type is not public!
I returned an internal class as I used Visual Studio to generate me the type.
internal class --> public class
Visual Studio 2017 or 2019 is totally unthoughtful on this, because Visual Studio itself requires the output to be in json format, while Visual Studio's default format is "XmlFormat" (config.Formatters.XmlFormatter).
Visual Studio should do this automatically instead of giving developers so much trouble.
To correct this problem, go to the WebApiConfig.cs file, and add
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
after "config.MapHttpAttributeRoutes();" in the Register(HttpConfiguration config) method. This would allow your project to produce json output.
In my case I solved recreating the database.
I made some changes in a model and launching Update-Database in Package Manager Console I got the following Error:
"The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_dbo.Activities_dbo.Projects_ProjectId". The conflict occurred in database "TrackEmAllContext-20190530144302", table "dbo.Projects", column 'Id'."
In case: If adding code to WebApiConfig.cs or Global.asax.cs doesn't work for you:
.ToList();
Add .ToList() function.
I tried out every solution but following worked for me:
var allShops = context.shops.Where(s => s.city_id == id)**.ToList()**;
return allShops;
I hope, it helps.
in my case, it was fixed when I removed the virtual keyword before my navigation properties,
I mean the reference tables.
so I changed
public virtual MembershipType MembershipType { get; set; }
to:
public MembershipType MembershipType { get; set; }

'ObjectContent`1' type failed to serialize the response body for content type 'text/xml; charset=utf-8' [duplicate]

I was working on ASP.NET MVC web API, I'm having this error:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
My controller is:
public Employee GetEmployees()
{
Employee employees = db.Employees.First();
return employees;
}
why I m getting this error?
For me this was a problem with circular referencing.
The accepted answer did not work for me because it only changes the behaviour of the JSON formatter, but I was getting XML when I called the service from the browser.
To fix this, I switched off XML and forced only JSON to be returned.
In the Global.asax file, put the following lines at the top of your Application_Start method:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Now only JSON results will be returned. If you need XML results, you will need to find a different solution.
in your global.asax file, in the Application_start() method add this line:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
I hope that helps you!
I got the same problem. And I solved it. I put the default constructor to the DTO class.
Ex:
public class User
{
public User()
{
}
}
Hope it work with you!
Put this in constructor. Hope this solve the problem:
public MyController()
{
db.Configuration.ProxyCreationEnabled = false;
}
I found two solutions to this. The first and easiest to implement is to change any IEnumerables, ICollections to a type of List. The WebAPI can serialize this objects, it however cannot serialize interface types.
public class Store
{
[StringLength(5)]
public string Zip5 { get; set; }
public virtual List<StoreReport> StoreReports { get; set; } //use a list here
}
The other option is to not use the native JSON serializer and run this override in the Register method of the WebApi Config:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
Solution is simple.
After LINQ query add .ToList() (or ToDictionary if need).
It will do eager loading than lazy loading of the data
**
this bug occur when calling from request web api/wcf/... from client side, but as side effect, you will need to include depending relations by include keyword.
**
public CustomerPortalContext()
: base("Name=CustomerPortalContext")
{
base.Configuration.ProxyCreationEnabled = false;
}
If you are working with EF, besides adding the code below on Global.asax
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Dont`t forget to import
using System.Data.Entity;
Then you can return your own EF Models
please check the web api documentation for this problem, Handling Circular Object References
Regards
If you use web api with Entity Framework, a solution can be
Failed to serialize the response in Web API with Json
Basically, you need to create a model corresponding to each EF model, this removes dependencies between classes and allow easy serialization.
Code: (taken from the referenced link)
Create a UserModel
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Change my method GetAll()
public IEnumerable<UserModel> GetAll()
{
using (Database db = new Database ())
{
List<UserModel> listOfUsers = new List<UserModel>();
UserModel userModel = new UserModel();
foreach(var user in db.Users)
{
userModel.FirstName = user.FirstName;
userModel.LastName = user.LastName;
listOfUsers.Add(userModel);
}
IEnumerable<UserModel> users = listOfUsers;
return users;
}
}
Default Entity 6 use XML to apis, in your project, find the file "Global.asax" File and add this line:
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
This line remove the XML Formatter.
but if you found this problem with other entities/classes, you have to create a new DTO for each class, and if you have a lot of them, you can find a problem, also I think that create a DTO only for solving this problem is no the best way...
Did you try this?
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
Regards
hmmm, Following may help.
I was getting the same exception, and in my case I was passing the actual poco entity created for entity code first. Since, it contains relation with other entities, I just created the viewmapper/dto entity on top of it to return.
It works fine now.
Poco Entity:
public class Tag
{
public int Id{get;set;}
public string Title{get;set;}
public IList<Location> Locations{get;set;}
}
ViewMapper/Dto
public class TagResultsViewMapper
{
public int Id{get;set;}
public string Title{get;set;}
//just remove the following relationship
//public IList<Location> Locations{get;set;}
}
Your question is quite similar to mine. You must not return data from database directly. For this, you must create Model and associate data you want show.
In my example, There are data about User that Json couldn't serialize, I had create a userModel and, in my API, I return userModel instead User from database.
The logic of convert or associate data between User and UserModel must be in API.
Failed to serialize the response in Web API with Json
This was the specific error I was getting back from my odata Web API call:
The 'ObjectContent`1' type failed to serialize the response
body for content type 'application/json; odata.metadata=minimal'.
I finally figured out that my dbContext class had a poorly formatted table name being assigned in onModelCreating.. so the SqlClient was dying looking for a table that didn't exist in my db!!

Prevent AutoMapper to treat string as collection in LINQ ProjectTo

I've the following set of classes to map (only in one direction, from Data* to Api*):
// Top level
public class DataEntity
{
public NestedDataEntity Nested { get; set; }
// ... other primitive/complex properties
}
public class ApiEntity
{
public NestedApiEntity Nested { get; set; }
// ... other primitive/complex properties
}
// Nested level
public class NestedDataEntity
{
public string Items { get; set; }
}
public class NestedApiEntity
{
public IEnumerable<ApiSubItem> Items { get; set; }
}
public class ApiSubItem
{
// there are properties here. Not needed for the sake of example
}
Mapping is configured within a Profile as in the following bit of code:
// mapping profile
public class MyCustomProfile : Profile
{
public MyCustomProfile()
{
CreateMap<DataEntity, ApiEntity>();
CreateMap<NestedDataEntity, NestedApiEntity>();
CreateMap<string, IEnumerable<ApiSubItem>>()
.ConvertUsing<TextToSubItemsConverter>();
}
}
// type converter definition
public class TextToSubItemsConverter :
ITypeConverter<string, IEnumerable<ApiSubItem>>
{
public IEnumerable<ApiSubItem> Convert(
string dataItems, IEnumerable<ApiSubItem> apiItems, ResolutionContext context)
{
// actually, deserialize & return an ApiSubItem[]
// here just return some fixed array
return new ApiSubItem[]
{
new ApiSubItem(),
new ApiSubItem(),
new ApiSubItem(),
};
}
}
// Main
public class Program
{
public static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MyCustomProfile>();
});
Mapper.AssertConfigurationIsValid();
DataEntity dataEntity = new DataEntity()
{
Nested = new NestedDataEntity()
{
Items = "Ignored text",
},
};
// This maps ok, no issues
ApiEntity apiEntity = Mapper.Map<DataEntity, ApiEntity>(dataEntity);
IQueryable<DataEntity> dataEntities = new[] { dataEntity }.AsQueryable();
// This exposes the System.Char to ApiSubItem issue
ApiEntity apiEntityProjected = dataEntities.ProjectTo<ApiEntity>().First();
}
}
Mapping configuration passes initial validation at startup, but then when an actual mapping is required I get exception:
System.InvalidOperationException: Missing map from System.Char to my.whatever.namespace.ApiSubItem. Create using Mapper.CreateMap.
If I omit completely the configuration between string and IEnumerable<ApiSubItem>, initial validation complains about the same context:
Context:
Mapping from type System.Char to my.whatever.namespace.ApiSubItem
although when added, it seems like it's not picked up by AutoMapper.
The mapping happens in a static context, through a ProjectTo<ApiEntity>() call on top of LINQ query over DbSet<DataEntity>. I've checked that converter does not require any dependency, just in case.
AutoMapper is 5.0.2, running in an MVC API web application under ASP.Net Core 1 RTM.
I've checked similar issues here on SO, but with no luck. Does anyone know how to make this type of scenario work? I guess I'm not the first one trying to (Auto)map from a string to a collection. TA
EDIT Added both failing and non-failing case to example
EDIT 2 After further search, somewhere someone suggested to ignore the field and perform mapping in AfterMap(). With this profile, exception is not thrown but resulting Items field is null:
CreateMap<DataEntity, ApiEntity>();
CreateMap<NestedDataEntity, NestedApiEntity>()
.ForMember(api => api.Items, options => options.Ignore())
.AfterMap((data, api) =>
{
api.Items = Mapper.Map<IEnumerable<ApiSubItem>>(data.Items);
});
CreateMap<string, IEnumerable<ApiSubItem>>()
.ConvertUsing<TextToSubItemsConverter>();
EDIT 3 Reworded question title to make it more specific for projection
I guess the actual answer is along the lines of "projections and converter/resolvers don't play nicely together". From AutoMapper Queryable.Extensions wiki:
Not all mapping options can be supported, as the expression generated must be interpreted by a LINQ provider. [...]
Not supported:
[...]
Before/AfterMap,
Custom resolvers,
Custom type converters
EDIT Workaround:
In this particular scenario, I've later found to be useful having an intermediate DTO entity that could grab some more fields, not needed for the final API entity but still needed for business logic.
In such DTO entity I left the string field as in DataEntity, so I could use projection and let AutoMapper/LinqToSQL grab only needed fields from DB.
Then, between DTO entity and API entity already in memory, I could apply a second mapping which also took advantage of custom converter for string ==> IEnumerable<ApiSubItem> mapping. HTH

Projection using contextual values in AutoMapper

I'm currently evaluation whether AutoMapper can be of benefit to our project. I'm working on a RESTful Web API using ASP.NET Web API, and one of the things I must return is a resource that contains links. Consider this simplified example, using the following domain object:
public class Customer
{
public string Name { get; set; }
}
I need to map this into a resource object, sort of like a DTO but with added properties to facilitate REST. This is what my resource object may look like:
public class CustomerResource
{
public string Name { get; set; }
public Dictionary<string, string> Links { get; set; }
}
The Links property will need to contain links to related resources. Right now, I could construct them using the following approach:
public IEnumerable<CustomerResource> Get()
{
Func<Customer, CustomerResource> map = customer =>
new CustomerResource
{
Name = customer.Name,
Links = new Dictionary<string, string>()
{
{"self", Url.Link("DefaultApi", new { controller = "Customers", name = customer.Name })}
}
}
var customers = Repository.GetAll();
return customers.Select(map);
}
...but this is pretty tedious and I have a lot of nested resources and such. The problem that I see is that I can't use AutoMapper because it doesn't let me provide certain things needed during projection that are scoped to the point where the mapping operation is performed. In this case, the Url property of the ApiController provides the UrlHelper instance that I need to create the links for me, but there may be other cases.
How would you solve this conundrum?
P.S. I typed up this code specifically for this question, and it compiled in your head but may fail in your favorite IDE.
This is not a pretty solution, but after reading through the docs it appears that there isn't one... We're currently throwing in contextual stuff by mapping Tuple<TDomainType, TContextStuff> to TDataTransfer. So in your case you'd Mapper.CreateMap<Tuple<Customer, Controller>, CustomerResource>.
Not pretty, but it works.
I would look in to using a Custom Type Converter. The type converter could have contextual information injected via an IOC container. Or, since the converter is instantiated at configuration time, it could have a reference to a factory which would return contextual information each time the type converter is run.
Simple Example
You could define an interface for getting your current "context" (what that means depends on what you're doing and how you implement things so for this example I'll just the current HttpContext which gets you access to Session, Server, Items, etc...):
public interface IContextFactory
{
HttpContext GetContext();
}
And the implementation is simply:
public class WebContextFactory : IContextFactory
{
public HttpContext GetContext()
{
return HttpContext.Current;
}
}
Your custom type converter could take an instance of IContextFactory from your IOC container and each time the mapping is run, you can call GetContext() to get the context for the current request.
Accessing the Url Property
The UrlHelper comes from the Request object attached to the current controller's context. Unfortunately, that is not available in the HttpContext. However, you could override the Initialize method on your ApiController and store the controllerContext in the HttpContext.Items collection:
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
HttpContext.Current.Items["controllerContext"] = controllerContext;
base.Initialize(controllerContext);
}
You can then access that from the current HttpContext:
var helper = ((HttpControllerContext) HttpContext.Current.Items["controllerContext"]).Request.GetUrlHelper();
I'm not sure it's the best solution, but it can get you the UrlHelper instance inside your custom type mapper.

Circular References and ScriptIgnore problems

I have several BusinessObject classes that refer to each other and I need to serialize one in a JsonResponse and return it to my view. I keep getting a circular reference exception and I cannot get rid of it. I have placed the [ScriptIgnore()] decorator on every property that is not a simple data type property and I am still getting the exception. I cant figure out where the problem is, as I am blocking the serializer from just about everything and it is still blowing up on me.
Is there any way to see what they current state of the serialized object is?
[HttpPost]
public JsonResult GetAnalysisInfo(int id)
{
ProjectContext myDB = db;
SearchAnalysis searchanalysis = SearchAnalysis.Find(myDB, id);
//searchanalysis.Results = searchanalysis.SearchResultsPrototype(myDB);
return this.Json(searchanalysis);
}
Update
I have also tried implementing ISerializable to no avail. My GetObjectData is very simple:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("SearchAnalysisId", this.SearchAnalysisId);
info.AddValue("Created", this.Created);
}
Still getting a CircularRefernce error. DateTime data types don't cause problems with Serialization right?
What I'm doing to prevent that error is to return an anonymouse type that reflects my object's properties like this :
public JsonResult CompanyListJson()
{
var list = (from companies in appContext.Companies
where companies.Active.Equals(true)
select new
{
Name = companies.DbName,
Id = companies.Id,
Active = companies.Id.Equals(CurrentCompany.Id)
});
return Json(list, JsonRequestBehavior.AllowGet);
}
Maybe it's not the best way, but it allows me to keep my JSON thin and push only the data I need ( and avoid that circular reference exception of course )
Looking at your sample, I would select new anonymouse type from SearchAnalysis, taking the properties I need. That should work

Categories