Linq to SQL using Repository Pattern: Object has no supported translation to SQL - asp.net-mvc

I have been scratching my head all morning behind this but still haven't been able to figure out what might be causing this.
I have a composite repository object that references two other repositories. I'm trying to instantiate a Model type in my LINQ query (see first code snippet).
public class SqlCommunityRepository : ICommunityRepository
{
private WebDataContext _ctx;
private IMarketRepository _marketRepository;
private IStateRepository _stateRepository;
public SqlCommunityRepository(WebDataContext ctx, IStateRepository stateRepository, IMarketRepository marketRepository)
{
_ctx = ctx;
_stateRepository = stateRepository;
_marketRepository = marketRepository;
}
public IQueryable<Model.Community> Communities
{
get
{
return (from comm in _ctx.Communities
select new Model.Community
{
CommunityId = comm.CommunityId,
CommunityName = comm.CommunityName,
City = comm.City,
PostalCode = comm.PostalCode,
Market = _marketRepository.GetMarket(comm.MarketId),
State = _stateRepository.GetState(comm.State)
}
);
}
}
}
The repository objects that I'm passing in look like this
public class SqlStateRepository : IStateRepository
{
private WebDataContext _ctx;
public SqlStateRepository(WebDataContext ctx)
{
_ctx = ctx;
}
public IQueryable<Model.State> States
{
get
{
return from state in _ctx.States
select new Model.State()
{
StateId = state.StateId,
StateName = state.StateName
};
}
}
public Model.State GetState(string stateName)
{
var s = (from state in States
where state.StateName.ToLower() == stateName
select state).FirstOrDefault();
return new Model.State()
{
StateId = s.StateId,
StateName = s.StateName
};
}
AND
public class SqlMarketRepository : IMarketRepository
{
private WebDataContext _ctx;
public SqlMarketRepository(WebDataContext ctx)
{
_ctx = ctx;
}
public IQueryable<Model.Market> Markets
{
get
{
return from market in _ctx.Markets
select new Model.Market()
{
MarketId = market.MarketId,
MarketName = market.MarketName,
StateId = market.StateId
};
}
}
public Model.Market GetMarket(int marketId)
{
return (from market in Markets
where market.MarketId == marketId
select market).FirstOrDefault();
}
}
This is how I'm wiring it all up:
WebDataContext ctx = new WebDataContext();
IMarketRepository mr = new SqlMarketRepository(ctx);
IStateRepository sr = new SqlStateRepository(ctx);
ICommunityRepository cr = new SqlCommunityRepository(ctx, sr, mr);
int commCount = cr.Communities.Count();
The last line in the above snippet is where it fails. When I debug through the instantiation (new Model.Community), it never goes into any of the other repository methods. I do not have a relationship between the underlying tables behind these three objects. Would this be the reason that LINQ to SQL is not able to build the expression tree right?

These are non-hydrated queries, not fully-hydrated collections.
The Communities query differs from the other two because it calls methods as objects are hydrated. These method calls are not translatable to SQL.
Normally this isn't a problem. For example: if you say Communities.ToList(), it will work and the methods will be called from the objects as they are hydrated.
If you modify the query such that the objects aren't hydrated, for example: when you say Communities.Count(), linq to sql attempts to send the method calls into the database and throws since it cannot. It does this even though those method calls ultimately would not affect the resulting count.
The simplest fix (if you truly expect fully hydrated collections) is to add ToList to the community query, hydrating it.

Try adding another repository method that looks like this:
public int CommunitiesCount()
{
get { return _ctx.Communities.Count(); }
}
This will allow you to return a count without exposing the entire object tree to the user, which is what I think you're trying to do anyway.
As you may have already guessed, I suspect that what you are calling the anonymous types are at fault (they're not really anonymous types; they are actual objects, which you are apparently partially populating in an effort to hide some of the fields from the end user).

Related

MVC Full Calendar Error [duplicate]

I am trying to do a simple JSON return but I am having issues I have the following below.
public JsonResult GetEventData()
{
var data = Event.Find(x => x.ID != 0);
return Json(data);
}
I get a HTTP 500 with the exception as shown in the title of this question. I also tried
var data = Event.All().ToList()
That gave the same problem.
Is this a bug or my implementation?
It seems that there are circular references in your object hierarchy which is not supported by the JSON serializer. Do you need all the columns? You could pick up only the properties you need in the view:
return Json(new
{
PropertyINeed1 = data.PropertyINeed1,
PropertyINeed2 = data.PropertyINeed2
});
This will make your JSON object lighter and easier to understand. If you have many properties, AutoMapper could be used to automatically map between DTO objects and View objects.
I had the same problem and solved by using Newtonsoft.Json;
var list = JsonConvert.SerializeObject(model,
Formatting.None,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
return Content(list, "application/json");
This actually happens because the complex objects are what makes the resulting json object fails.
And it fails because when the object is mapped it maps the children, which maps their parents, making a circular reference to occur. Json would take infinite time to serialize it, so it prevents the problem with the exception.
Entity Framework mapping also produces the same behavior, and the solution is to discard all unwanted properties.
Just expliciting the final answer, the whole code would be:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
new {
Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
}
, JsonRequestBehavior.AllowGet
);
}
It could also be the following in case you don't want the objects inside a Result property:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
(from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
, JsonRequestBehavior.AllowGet
);
}
To sum things up, there are 4 solutions to this:
Solution 1: turn off ProxyCreation for the DBContext and restore it in the end.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
bool proxyCreation = db.Configuration.ProxyCreationEnabled;
try
{
//set ProxyCreation to false
db.Configuration.ProxyCreationEnabled = false;
var data = db.Products.ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
finally
{
//restore ProxyCreation to its original state
db.Configuration.ProxyCreationEnabled = proxyCreation;
}
}
Solution 2: Using JsonConvert by Setting ReferenceLoopHandling to ignore on the serializer settings.
//using using Newtonsoft.Json;
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.ToList();
JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Following two solutions are the same, but using a model is better because it's strong typed.
Solution 3: return a Model which includes the needed properties only.
private DBEntities db = new DBEntities();//dbcontext
public class ProductModel
{
public int Product_ID { get; set;}
public string Product_Name { get; set;}
public double Product_Price { get; set;}
}
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new ProductModel
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Solution 4: return a new dynamic object which includes the needed properties only.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
JSON, like xml and various other formats, is a tree-based serialization format. It won't love you if you have circular references in your objects, as the "tree" would be:
root B => child A => parent B => child A => parent B => ...
There are often ways of disabling navigation along a certain path; for example, with XmlSerializer you might mark the parent property as XmlIgnore. I don't know if this is possible with the json serializer in question, nor whether DatabaseColumn has suitable markers (very unlikely, as it would need to reference every serialization API)
add [JsonIgnore] to virtuals properties in your model.
Using Newtonsoft.Json: In your Global.asax Application_Start method add this line:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Its because of the new DbContext T4 template that is used for generating the EntityFramework entities. In order to be able to perform the change tracking, this templates uses the Proxy pattern, by wrapping your nice POCOs with them. This then causes the issues when serializing with the JavaScriptSerializer.
So then the 2 solutions are:
Either you just serialize and return the properties you need on the client
You may switch off the automatic generation of proxies by setting it on the context's configuration
context.Configuration.ProxyCreationEnabled = false;
Very well explained in the below article.
http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/
Provided answers are good, but I think they can be improved by adding an "architectural" perspective.
Investigation
MVC's Controller.Json function is doing the job, but it is very poor at providing a relevant error in this case. By using Newtonsoft.Json.JsonConvert.SerializeObject, the error specifies exactly what is the property that is triggering the circular reference. This is particularly useful when serializing more complex object hierarchies.
Proper architecture
One should never try to serialize data models (e.g. EF models), as ORM's navigation properties is the road to perdition when it comes to serialization. Data flow should be the following:
Database -> data models -> service models -> JSON string
Service models can be obtained from data models using auto mappers (e.g. Automapper). While this does not guarantee lack of circular references, proper design should do it: service models should contain exactly what the service consumer requires (i.e. the properties).
In those rare cases, when the client requests a hierarchy involving the same object type on different levels, the service can create a linear structure with parent->child relationship (using just identifiers, not references).
Modern applications tend to avoid loading complex data structures at once and service models should be slim. E.g.:
access an event - only header data (identifier, name, date etc.) is loaded -> service model (JSON) containing only header data
managed attendees list - access a popup and lazy load the list -> service model (JSON) containing only the list of attendees
Avoid converting the table object directly. If relations are set between other tables, it might throw this error.
Rather, you can create a model class, assign values to the class object and then serialize it.
I'm Using the fix, Because Using Knockout in MVC5 views.
On action
return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));
function
public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
{
TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
foreach (var item in Entity.GetType().GetProperties())
{
if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
item.SetValue(Entity_, Entity.GetPropValue(item.Name));
}
return Entity_;
}
You can notice the properties that cause the circular reference. Then you can do something like:
private Object DeCircular(Object object)
{
// Set properties that cause the circular reference to null
return object
}
//first: Create a class as your view model
public class EventViewModel
{
public int Id{get;set}
public string Property1{get;set;}
public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
var events = await db.Event.Find(x => x.ID != 0);
List<EventViewModel> model = events.Select(event => new EventViewModel(){
Id = event.Id,
Property1 = event.Property1,
Property1 = event.Property2
}).ToList();
return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}
An easier alternative to solve this problem is to return an string, and format that string to json with JavaScriptSerializer.
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
It is important the "Select" part, which choose the properties you want in your view. Some object have a reference for the parent. If you do not choose the attributes, the circular reference may appear, if you just take the tables as a whole.
Do not do this:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.toList();
return j.Serialize(entityList );
}
Do this instead if you don't want the whole table:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
This helps render a view with less data, just with the attributes you need, and makes your web run faster.

Linq casting Issue in EntityFramework

Hi,
I am new to Linq and entity framework. I am doing something like this
I have 3 viewmodel:
1.
public class FlowViewModel
{
..........................
public List<FlowLevelViewModel> Levels { get; set; }
}
public class FlowLevelViewModel
{
.........................
public List<BlockDetailsViewmodel> Blocks { get; set; }
}
public class BlockDetailsViewmodel
{
.......................
}
and from my controller I am calling the datalayer.
var model = new FlowViewModel();
model = dataOb.GetFlowForTheDocument(company, docType);
model = dataOb.GetFlowStageForTheDocument(model);
return model;
and in my datalayer
public FlowViewModel GetFlowStageForTheDocument(FlowViewModel model)
{
var flowlevelviewModel = (from p in dbContext.FlowStages
where p.FlowID == model.FlowId
select new FlowLevelViewModel()
{
.................
Blocks = GetBlockDetailsForTheDocument(p.StageID, .StageType)
}).ToList();
model.Levels = flowlevelviewModel;
return model;
}
public List<BlockDetailsViewmodel> GetBlockDetailsForTheDocument(int StageID, string stageType)
{
var blockDetails = new List<BlockDetailsViewmodel>();
......................................
return blockDetails;
}
While I am running the program I am getting this error:
**NotSupportedException Was unhandled by user Code**
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[SEADViewModel.BlockDetailsViewmodel] GetBlockDetailsForTheDocument(Int32, System.String)' method, and this method cannot be translated into a store expression.
My project is in production stage so I have no time at all. Does anyone know what I am doing wrong?
This should solve your problem:
var data = (from p in dbContext.FlowStages
where p.FlowID == model.FlowId
select p).ToList();
var flowlevelviewModel = (from p in data
select new FlowLevelViewModel()
{
.................
Blocks = GetBlockDetailsForTheDocument(p.StageID, .StageType)
}).ToList();
Note that this will evaluate the query at the first ToList(). If you need to run the entire query at once, you need to build a simple LINQ expression, you can't use your method GetBlockDetailsForTheDocument inside the query. See #Tilak's answer for a link to supported build in methods.
You are using Linq to Entities.
It does not support all the functions. List of supported and non supported functions
You need to write custom model defined function GetBlockDetailsForTheDocument to use it in LINQ query.

Add relationship to many to many in entity framework code first

I want to add a relationship between multiple existing entities and another existing entity. Here is my model:
public class Term
{
public int TermId { get; set; }
public virtual ICollection<SubForm> SubForms { get; set; }
}
public class SubForm
{
public int SubFormId { get; set; }
public virtual ICollection<Term> Terms { get; set; }
}
I have an update repository method as follows:
public IQueryable<Term> GetTerms()
{
IQueryable<Term> query = db.Terms.AsNoTracking();
return query;
}
public Term UpdateTerm(Term term, IEnumerable<Expression<Func<Term, object>>> properties)
{
if (term.TermId == 0)
{
throw new InvalidOperationException("Term does not exist");
}
db.Terms.Attach(term);
if (properties != null)
{
foreach (var selector in properties)
{
string propertyName = Helpers.PropertyToString(selector.Body);
db.Entry(term).Property(propertyName).IsModified = true;
}
}
db.SaveChanges();
return term;
}
Now I assume this would work when I make this call in my service layer:
public void AddFormToTerm(int termId, int formId)
{
var term = termsRepository.GetTerms().FirstOrDefault(t => t.TermId == termId);
var subForms = termsRepository.GetSubForms().Where(t => t.FormId == formId);
//I assume this would work by adding existing forms to an existing term.
foreach (var subForm in subForms)
{
term.SubForms.Add(subForm);
}
termsRepository.UpdateTerm(term, null);
}
Unfortunately, this doesn't get updated, there is nothing in the intermediate table when I checked the database. No exception was also thrown.
Using AsNoTracking in this case is the problem. Without AsNoTracking it will work. You must keep in mind that you can update a many-to-many relationship only with the change tracking mechanism. But in your code the EF context will know about term and the SubForms collection for the first time when you call Attach in your UpdateTerm method. EF does not notice that you did add the SubForms to the term because those entities were not attached to the context (since you used AsNoTracking = "EF, please do not attach to the context!"). But after Attach nothing happened anymore before you called SaveChanges = No change = No database commands.
So removing AsNoTracking (or creating another method or a parameter to load with tracking) is the best option. Everything else will involve ugly "tricks" like this:
public Term UpdateTerm(Term term, ...)
{
//...
// Restore the state before adding the subforms = current state in DB
var tempSubForms = term.SubForms;
term.SubForms = null;
// Inform EF about this state = term exists, subforms exist
// in DB but no relationships
db.Terms.Attach(term);
foreach (var subForm in tempSubForms)
db.SubForms.Attach(subForm);
// Change the state: EF change tracking recognizes this
term.SubForms = tempSubForms;
//...
// EF now will send INSERT statements for the join table
db.SaveChanges();
return term;
}

Enity Framework 4.1 - One trip database update

Let's say I have this code:
class Score
{
public Update(int score)
{
update score but do not call (context.SaveChanges())
}
}
class Foo
{
public DoSomething(int update)
{
Score score = new Score();
score.Update(2);
SomeObj obj = (select object);
obj.Soo = 3;
context.SaveChanges();
}
}
Basically to make it work, I need to explicity provide SaveChanges in method Update. But when I have 4 such methods in row, and 34243 users want to update data, I don't think saving for each one in 4 trips would be a good idea.
Is there way in EF4.1 to delay database update the last moment, in provided example, Or I'm forced to explicity save for each method ?
EDIT:
For clarification. I tried to do not call SaveChanges in external method, and only one time where the changes mu be saved.
I will give an real example:
public class ScoreService : IScoreService
{
private JamiContext _ctx;
private IRepository<User> _usrRepo;
public ScoreService(IRepository<User> usrRepo)
{
_ctx = new JamiContext();
_usrRepo = usrRepo;
}
public void PostScore(int userId, GlobalSettings gs, string name)
{
User user = _ctx.UserSet.Where(x => x.Id == userId).FirstOrDefault();
if (name == "up")
{
user.Rating = user.Rating + gs.ScoreForLike;
}
else if (name == "down")
{
user.Rating = user.Rating - Math.Abs(gs.ScoreForDislike);
}
}
}
And Now:
public PostRating LikeDislike(User user, int postId, int userId, GlobalSettings set, string name)
{
PostRating model = new PostRating();
var post = (from p in _ctx.PostSet
where p.Id == postId
select p).FirstOrDefault();
if (name == "up")
{
post.Like = post.Like + 1;
model.Rating = post.Like - post.Dislike;
}
else if (name == "down")
{
post.Dislike = post.Dislike + 1;
model.Rating = post.Like - post.Dislike;
}
PostVote pv = new PostVote();
pv.PostId = post.Id;
pv.UserId = user.Id;
_ctx.PostVoteSet.Add(pv);
_scoreSrv.PostScore(userId, set, name);
_ctx.SaveChanges();
return model;
}
I this case user rating do not update, Until I call SaveChanges in PostScore
In your example it looks like PostScore and LikeDislike use different context instances. That is the source of your problem and there is no way to avoid calling multiple SaveChanges in that case. The whole operation is single unit of work and because of that it should use single context instance. Using multiple context instances in this case is wrong design.
Anyway even if you call single SaveChanges you will still have separate roundtrip to the database for each updated, inserted or deleted entity because EF doesn't support command batching.
The way to delay database update to the last moment is by not calling SaveChanges until the last moment.
You have complete control over this code, and if your code is calling SaveChanges after every update, then that needs changing.
This not really solves my entire problem, but at least I can use single instance of Context:
With Ninject:
Bind<JamiContext>().To<JamiContext>().InRequestScope();
And then constructor:
private JamiContext _ctx;
private IRepository<User> _usrRepo;
public ScoreService(IRepository<User> usrRepo, JamiContext ctx)
{
_ctx = ctx;
_usrRepo = usrRepo;
}

Entity Framework 4 and Repository Pattern problem

I am having trouble understanding if I am doing this correctly or not. I have 3 entities that are dependent on each other. I am trying to add new objects to these entities and then call save changes ultimately adding the corresponding records to the tables honoring the FK constraints.
I am getting the error:
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
In my code I am parsing some XML with linq while adding the new objects to the context as I go. In my service layer I have the following method to handle processing the incoming data.
public void ProcessSurvey(int surveyContentId, int caseNo, string surveyTitle, string reportVersion, string reportXml)
{
// get surveyid
var surveyContent = _surveyContentRepository.GetSurveyContent(surveyContentId);
// create response obj
var surveyResponse = new SurveyResponse()
{
SurveyId = surveyContent.SurveyId,
CaseNo = caseNo,
SurveyTitle = surveyTitle,
ReportVersion = reportVersion,
Created = DateTime.Now,
ResponseXML = reportXml
};
// add response obj to context?
_surveyResponseRepository.Add(surveyResponse);
// get the questions elements from the xml data
var questions = SurveyResponseHelper.GetResponseQuestions(reportXml);
// iterate over questions
foreach (XElement question in questions)
{
SurveyQuestion thisSurveyQuestion = SurveyResponseHelper.ProcSurveyQuestion(question, surveyContentId);
// add question?
_surveyQuestionRepository.Add(thisSurveyQuestion);
// get question answer
SurveyAnswer thisSurveyAnswer = SurveyResponseHelper.GetAnswer(question);
//update the answer with the question and response obj to satisfy the FK reference
thisSurveyAnswer.SurveyQuestion = thisSurveyQuestion;
thisSurveyAnswer.SurveyResponse = surveyResponse; // This is where it breaks ERRROR: The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects
_surveyAnswerRepository.Add(thisSurveyAnswer);
}
//commit
_surveyAnswerRepository.Save();
}
My Repositories look like this..
public interface ISurveyAnswerRepository
{
void Add(SurveyAnswer surveyAnswer);
void Save();
}
public class SurveyAnswerRepository : Repository, ISurveyAnswerRepository
{
//private DiversionProgramsEntities _db;
public SurveyAnswerRepository()
{
//_db = new DiversionProgramsEntities();
}
public void Add(SurveyAnswer surveyAnswer)
{
this.DataContext.SurveyAnswers.AddObject(surveyAnswer);
}
public void Save()
{
this.DataContext.SaveChanges();
}
my base repository
public class Repository
{
private DiversionProgramsEntities _dataContext;
public DiversionProgramsEntities DataContext
{
get { return _dataContext ?? (_dataContext = DatabaseFactory.CreateContext()); }
}
}
and static class / method to create the context
public static class DatabaseFactory
{
public static DiversionProgramsEntities CreateContext()
{
return new DiversionProgramsEntities();
}
}
here is my helper code..
public class SurveyResponseHelper
{
public static IEnumerable<XElement> GetResponseQuestions(string xmlResponseData)
{
XElement xmlData = XElement.Parse(xmlResponseData);
var questions = from n in xmlData.Descendants()
where n.Parent.Name.LocalName == "questions"
select n;
return questions;
}
public static SurveyQuestion ProcSurveyQuestion(XElement question, int surveyContentId)
{
// get the question type
var questionType = question.Name.LocalName;
// get question element text. This is the actual question text
var questionText = question.Elements().Where(e => e.Name.LocalName == "direction").SingleOrDefault().Value;
// check to see if this question exists in the data table, if it does then we will use the questionid from that which will get used to tie the SurveyAnswer to this question.
// if question does not already exist then a new one will be created
SurveyQuestionRepository surveyQuestionRepository = new SurveyQuestionRepository();
SurveyQuestion surveyQuestion;
surveyQuestion = surveyQuestionRepository.GetSurveyQuestion(surveyContentId, questionType, questionText);
if (surveyQuestion == null)
{
surveyQuestion = new SurveyQuestion()
{
QuestionText = questionText,
QuestionType = questionType,
SurveyContentId = surveyContentId
};
}
return surveyQuestion;
}
public static SurveyAnswer GetAnswer(XElement question)
{
// get the answer index value
var answers = question.Elements().Where(e => e.Name.LocalName == "answers").SingleOrDefault();
int userAnswerIndex = Int32.Parse(answers.Attribute("userAnswerIndex").Value);
// move the answers to an array so we can use the index to get the correct answer
XElement[] answersArray = answers.Elements().ToArray();
SurveyAnswer answer = new SurveyAnswer()
{
AnswerText = answersArray[userAnswerIndex].Value
};
return answer;
}
}
It looks like the error is describing perfectly what is going on. In the following line:
var questions = SurveyResponseHelper.GetResponseQuestions(reportXml);
You are getting a question from another class. That class probably creates it's own object context.
You can't attach a question to the answer if they are from different object contexts.
To solve this, the easiest way is to add a parameter to your methods GetResponseQuestions for the datacontext, so your other method can use that the repositories datacontext to get the questions.
Also, various IoC methods would simplify this.
Where does your _surveyContentRepository come from? If it's static I could see a scenario where that holds on to a SurveyContent object which is attached to one DiversionProgramsEntities, and your ProcSurveyQuestion() method finds and returns an existing SurveyQuestion, attached to a different DiversionProgramsEntities.
Other than that, I think a general pointer I can give you is to assign objects to each other using the objects themselves rather than the object Ids, so instead of:
var surveyResponse = new SurveyResponse { SurveyId = surveyContent.SurveyId }
...use:
var surveyResponse = new SurveyResponse { Survey = surveyContent }
This automatically adds your new SurveyResponse object to the same object context to which the SurveyContent object belongs, and means you don't have to manually add anything to a repository. You can assemble your entire object graph like this, then call Save() on the repository you used to retrieve the first object to save the whole thing.
As #TimHoolihan stated the issue is that you are not using the same Data Context for accessing the Survey Responses and Survey Questions and actually I believe the issue lines in the line below from the ProcSurveyQuestion method.
SurveyQuestionRepository surveyQuestionRepository = new SurveyQuestionRepository();
I see that you have a singleton DataContext in the DiversionProgramsEntities class, but I cannot infer from your code if the SurveyQuestionRepository and SurveryResponseRepositories are also using that same context. Based on the error you are getting, I am guessing that they are using separate contexts, so again as #TimHoolihan suggested, you need to modify your code to use the same context for both.
You should also look into the UnitOfWork pattern as this is what you are trying to accomplish here, but you do not have a common context to track all of your changes across.

Resources