Reputation: 607
Background:
I have an API which retrieves data from a DB with two tables (Tokens and Area). Each Token has an AreaID which relates to an Area object. I'm using Entity Framework database first.
Problem:
When I try to query the database to get a Token (which should contain a reference to an Area). I get an error:
Error getting value from 'Area' on 'System.Data.Entity.DynamicProxies.Token_51B85DEDCE118919D07024535C24194E890BF0A8499689750190733A8008D739'."
Code:
Here are the models as created by EF:
public partial class Token
{
public System.Guid ID { get; set; }
public System.DateTime Expiry { get; set; }
public System.Guid Reference { get; set; }
public string IPAddress { get; set; }
public string TokenValue { get; set; }
public Nullable<System.Guid> Area_ID { get; set; }
public virtual Area Area { get; set; }
}
public partial class Area
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Area()
{
this.Tokens = new HashSet<Token>();
}
public System.Guid ID { get; set; }
public string Name { get; set; }
public string UriPrefix { get; set; }
public string Description { get; set; }
public System.DateTime Created { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Token> Tokens { get; set; }
}
This is where I query the DB (trimmed down):
using (var dbContext = new APIEntities())
{
try
{
tokenObject = dbContext.Tokens.Where(t => t.TokenValue == decryptedToken).FirstOrDefault();
// the tokenObject is created but the Area property is null.
} catch (Exception ex) {
string message = ex.Message.ToString();
}
}
Here is the method that serializes the JSON:
public HttpResponseMessage LogIntoAppWithToken()
{
string result = "";
string jsonString = "failed to get JSON";
string message = "";
try
{
var re = Request;
var headers = re.Headers;
EncryptedToken encToken = new EncryptedToken();
TokenChecker tokenChecker = new TokenChecker();
encToken.tokenValue = "";
if (headers.Contains("X-TOKEN"))
{
encToken.tokenValue = headers.GetValues("X-TOKEN").First();
}
result = tokenChecker.IsTokenValid(encToken).ToString();
Token retrievedToken = tokenChecker.GetTokenByTokenValue(encToken.tokenValue);
jsonString = JsonConvert.SerializeObject(retrievedToken);
// ERROR Here ^^
} catch (Exception ex) {
message = ex.Message.ToString();
}
return Request.CreateResponse(HttpStatusCode.OK, "Token is valid: " + result + ". " + " " + jsonString + " " + " Any error messages: " + message);
}
What I have tried:
When the dbContext is created I have used:
dbContext.Configuration.ProxyCreationEnabled = false;
dbContext.Configuration.LazyLoadingEnabled = false;
In the WebApiConfig:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
In the Application_start of Global.asax:
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
I have even tried getting the AreaID from the tokenObject and querying the DB to get the Area 'manually' and then adding this retrieved Area object to the tokenObject (overwriting the null version):
Area areaObject = dbContext.Areas.Where(a => a.ID == tokenObject.Area_ID).FirstOrDefault();
tokenObject.Area = areaObject;
but this results in a self referencing loop error. (this idea was more for testing than to use as a solution).
What do I need to do to be able to retrieve a Token that contains an Area as one of its properties and then serialize as a JSON string?
Edit 1
I notice that the Token object contains the Area object now but that Area object contains a Token which contains an Area? and this goes on and on...
With my setup, surely, the Token contains Areas but Areas should NOT contains Tokens?
Upvotes: 1
Views: 1636
Reputation: 375
If you don't want to serialize your referenced entities then go with one of below option.
To Avoid Lazy loading for all entities set LazyLoadingEnabled to false in your bdContext class.
public SchoolDBEntities(): base("name=SchoolDBEntities")
{
this.Configuration.LazyLoadingEnabled = false;
}
Don't worry you still have ability to access your referenced entity by calling Include methode. Example:-
var list = context.Customers
.Include(c => c.Invoices)
.ToList();
Upvotes: 0
Reputation: 607
After a LOT of searching around I found another answer that worked for me.
Simply add the [JsonIgnore] attribute to the affected property. For me on the Area model:
[JsonIgnore]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Token> Tokens { get; set; }
Upvotes: 2
Reputation: 41749
Use Include:
tokenObject = dbContext.Tokens
.Where(t => t.TokenValue == decryptedToken)
.Include(t => t.Area)
.FirstOrDefault();
Upvotes: 0