Entity Framework 4 and Repository Pattern problem - entity-framework-4

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.

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.

Adding record duplicates other object using entity framework

I am trying to add a new record in an MVC controller method using Entity framework.
When i just used "InsertOrUpdate" the audittype got duplicated. Based on the answer from Entity Framework adding record with a related object i hoped to fix it pretty qiock. This is the code I have right now:
Controller:
if (ModelState.IsValid)
{
Audit newAudit = Factory.GetNew();
newAudit.Name = model.Name;
newAudit.Deadline = model.Deadline;
newAudit.AuditType = auditTypeRepository.Find(model.SelectedAuditTypeId);
Repository.InsertOrUpdate(newAudit);
Repository.Save();
return RedirectToAction(MVC.Audits.Details(newAudit.Id));
}
Repository:
public override void InsertOrUpdate(Qdsa.WebApplications.AuditMaster.Data.Audit model)
{
if (model.Id == default(int))
{
// New entity
context.Audits.Add(model);
}
else
{
// Existing entity
model.ModifiedOn = DateTime.Now;
context.Entry(model).State = EntityState.Modified;
}
//If I leave out the code below the AuditType will be duplicated
if (model.AuditType != null)
{
context.Entry<AuditType>(model.AuditType).State = EntityState.Unchanged;
}
}
public virtual void Save()
{
context.SaveChanges();
}
So i thought I fixed the problem. However, AuditType has Child objects too. And now these childobjects get duplicated.
What is the right way to add entities with child objects which already exists?
Because the AuditType is required I can't save it without first and then update it. any suggestions?
UPDATE:
Both the AuditRepostory and the AuditTypeRepository inherit from BaseRepository which has the context as:
protected DBContext context = new DBContext ();
public virtual T Find(int id)
{
return All.SingleOrDefault(s => s.Id == id);
}
I can imagine two reasons for the problem:
Either auditTypeRepository.Find performs a no tracking query (with .AsNoTracking())
Or you are using a context instance per repository, so that Repository and auditTypeRepository are working with two different contexts which will indeed result in a duplication of the AuditType because you don't attach it to the the context that corresponds with Repository (except in the line with your comment).
If the latter is the case you should rethink your design and inject a single context instance into all repositories instead of creating it inside of the repositories.
I think the problem is from here:
newAudit.AuditType = auditTypeRepository.Find(model.SelectedAuditTypeId);
Change that like this:
newAudit.AuditTypeId = model.SelectedAuditTypeId;

Exclude property from updating when SaveChanges() is called

There appears to be two ways to update a disconnected Entity Framework entity using the "attach" method.
Method One is to simply set the disconnected entity's state as modified:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.SaveChanges();
This will save all fields on the "dog" object. But say you are doing this from an mvc web page where you only allow editing of Dog.Name, and the only Dog property contained on the page is Name. Then one could do Method Two:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).Property(o => o.Name).CurrentValue = dog.Name;
myDbContext.Entry(dog).Property(o => o.Name).IsModified = true;
myDbContext.SaveChanges();
Method Two could get quite verbose when there are a lot of properties to update. This prompted me to attempt Method Three, setting IsModified = false on the properties I don't want to change. This does not work, throwing the runtime error "Setting IsModified to false for a modified property is not supported":
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.Entry(dog).Property(o => o.Owner).IsModified = false;
myDbContext.SaveChanges();
I'd much prefer to use Method One everywhere, but there are many instances where my asp.net mvc view does not contain every scalar property of the Dog class.
My questions are:
Are there any attributes I could use on the POCO class that would tell Entity Framework that I never want the property to up updated? Eg, [NeverUpdate]. I am aware of the [NotMapped] attribute, but that is not what I need.
Failing that, is there any way I can use Method One above (myDbContext.Entry(dog).State = EntityState.Modified;
) and exclude fields that I don't want updated?
P.S. I am aware of another way, to not use "attach" and simply fetch a fresh object from the database, update the desired properties, and save. That is what I am doing, but I'm curious if there is a way to use "attach," thus avoiding that extra trip to the database, but do it in a way that is not so verbose as Method Two above. By "fetch a fresh object" I mean:
Dog dbDog = myDbContext.Dogs.FirstOrDefault(d => d.ID = dog.ID);
dbDog.Name = dog.Name;
myDbContext.SaveChanges();
The following may work works.
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
var objectContext = ((IObjectContextAdapter) myDbContext).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(Dogs)))
{
// You need to give Foreign Key Property name
// instead of Navigation Property name
entry.RejectPropertyChanges("OwnerID");
}
myDbContext.SaveChanges();
If you want to do it in a single line, use the following extension method:
public static void DontUpdateProperty<TEntity>(this DbContext context, string propertyName)
{
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(TEntity)))
{
entry.RejectPropertyChanges(propertyName);
}
}
And use it like this
// After you modify some POCOs
myDbContext.DontUpdateProperty<Dogs>("OwnerID");
myDbContext.SaveChanges();
As you can see, you can modify this solution to fit your needs, e.g. use string[] properties instead of string propertyName as the argument.
Suggested Approach
A better solution would be to use an Attribute as you suggested ([NeverUpdate]). To make it work, you need to use SavingChanges event (check my blog):
void ObjectContext_SavingChanges(object sender, System.Data.Objects.SavingChangesEventArgs e)
{
ObjectContext context = sender as ObjectContext;
if(context != null)
{
foreach(ObjectStateEntry entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
var type = typeof(entry.Entity);
var properties = type.GetProperties();
foreach( var property in properties )
{
var attributes = property.GetCustomAttributes(typeof(NeverUpdateAttribute), false);
if(attributes.Length > 0)
entry.RejectPropertyChanges(property.Name);
}
}
}
}
// Check Microsoft documentation on how to create custom attributes:
// http://msdn.microsoft.com/en-us/library/sw480ze8(v=vs.80).aspx
public class NeverUpdateAttribute: SystemAttribute
{
}
//In your POCO
public class Dogs
{
[NeverUpdate]
public int OwnerID { get; set; }
}
Warning: I did not compile this code. I'm not at home :/
Warning 2: I have just read the MSDN documentation and it says:
ObjectStateEntry.RejectPropertyChanges Method
Rejects any changes made to the property with the given name since the
property was last loaded, attached, saved, or changes were accepted.
The orginal value of the property is stored and the property will no
longer be marked as modified.
I am not sure what its behavior would be in the case of attaching a modified entity. I will try this tomorrow.
Warning 3: I have tried it now. This solution works. Property that is rejected with RejectPropertyChanges() method are not updated in the persistence unit (database).
HOWEVER, if the entity that is updated is attached by calling Attach(), the current context remains dirty after SaveChanges(). Assume that the following row exists in the database:
Dogs
ID: 1
Name: Max
OwnerID: 1
Consider the following code:
var myDog = new Dogs();
myDog.ID = 1;
myDog.Name = Achilles;
myDog.OwnerID = 2;
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
The current state of database after SaveChanges():
Dogs:
ID: 1
Name: Achilles
OwnerID: 1
The current state of myDbContext after SaveChanges():
var ownerId = myDog.OwnerID; // it is 2
var status = myDbContext.Entry(myDog).State; // it is Unchanged
So what you should do? Detach it after SaveChanges():
Dogs myDog = new Dogs();
//Set properties
...
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
myDbContext.Entry(myDog).State = EntityState.Detached;

EF Code First, how to reflect on model

In EF code first, one specifies field properties and relationships using the fluent interface. This builds up a model. Is it possible to get a reference to this model, and reflect on it?
I want to be able to retrieve for a given field, if it is required, what its datatype is, what length, etc...
You need to access the MetadataWorkspace. The API is pretty cryptic. You may want to replace DataSpace.CSpace with DataSpace.SSpace to get the database metadata.
public class MyContext : DbContext
{
public void Test()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var mdw = objectContext.MetadataWorkspace;
var items = mdw.GetItems<EntityType>(DataSpace.CSpace);
foreach (var i in items)
{
foreach (var member in i.Members)
{
var prop = member as EdmProperty;
if (prop != null)
{
}
}
}
}

Linq to SQL using Repository Pattern: Object has no supported translation to SQL

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).

Resources