I am having a difficult time trying to get my models correctly, is there anyway I can fit 2 models inside an IEnumerable? This is what I have..
public class threadreplying
{
public IEnumerable<profile,Threadpost> IEThreadpost { get; set; }
public taker orginalthread { get; set; }
}
The IEnumerable only takes 1 argument how can I make the IEthreadpost work? I am doing it like this because I am implenting a Join in my sql and require information from both models that are stored in a database. I have also tried to do this
public class ModelMix
{
public profile profiles { get; set; }
public Threadpost threadposts { get; set; }
}
public class threadreplying
{
public IEnumerable<ModelMix> IEThreadpost { get; set; }
public taker orginalthread { get; set; }
}
This works but it does not work inside the view it gives me a null error eventhough I am checking against that
#if (Model.IEThreadpost != null)
{
foreach (var item in Model.IEThreadpost)
{
#item.threadposts.post
}
}
You need to check #item.threadposts for null.
#if (Model.IEThreadpost != null)
{
foreach (var item in Model.IEThreadpost)
{
#if (item.threadposts != null)
{
#item.threadposts.post
}
}
}
Of course, you should recognize ahead of time what the implications of doing that check are, and you should ensure that this isn't an indication of your previous logic, where you actually do your select.
Related
I have a SimID query that joins a given list of cell phone numbers with their respective IDs in my DB, if the number doesn't exist in the DB it returns 0 as an ID. If I watch my query while debugging, it executes perfectly and very quickly. However when I loop through the results of the query and create a new object and add it to my model it takes 3 minutes to create 458 new objects.
I am new to EF and Linq what am I doing wrong? How can I optimize this code to execute faster?
Any help would be greatly appreciated.
//Query to create virtual table of SimID and MSISDN joined on Cache.MSISDN
var SimID = (from ca_msisdn in Cache.MSISDN
join db_simobj in ctx.Sims on ca_msisdn equals db_simobj.Msisdn into Holder
from msisdnresult in Holder.DefaultIfEmpty()
select new { MSISDN = ca_msisdn, ID = (msisdnresult == null || msisdnresult.SimId == 0 ? 0 : msisdnresult.SimId) });
//Loop through virtual tables and add new data to model
foreach (var ToUpdate in SimID)
{
if (ToUpdate.ID == 0)
{
Console.WriteLine("We have found a new MSISDN: " + ToUpdate.MSISDN + " adding it to the model.");
ctx.Sims.Add(new Sim { Msisdn = ToUpdate.MSISDN });
}
}
//My Sim object
public partial class Sim
{
public Sim()
{
this.CDR_Event = new HashSet<CDR_Event>();
}
public long SimId { get; set; }
public long SimStatusId { get; set; }
public Nullable<long> FitmentCentreId { get; set; }
public string Serial { get; set; }
public string Msisdn { get; set; }
public string Puk { get; set; }
public string Network { get; set; }
public Nullable<System.DateTime> SimStatusDate { get; set; }
public Nullable<System.DateTime> ActivationDate { get; set; }
public Nullable<System.DateTime> ExpiryDate { get; set; }
public Nullable<long> APNSimStatusId { get; set; }
public Nullable<System.DateTime> APNActivated { get; set; }
public Nullable<System.DateTime> APNConfirmed { get; set; }
public string Svr { get; set; }
public virtual ICollection<CDR_Event> CDR_Event { get; set; }
}
Your main problem is that every time that you add to the Sims collection on your context you are adding to what the context's change tracker needs to keep track of, which can become a very expensive proposition memory-wise when you start having larger amounts of entities.
You can solve this in one of two ways:
1) Save the data in batches. That way the change tracker never has to keep track of a large amount of added entities.
2) If you don't want to save it right away, keep it as an in-memory list first, and then do #1 when you do go to save those records.
The answer to this question as IronMan84 alluded to; as documented here is to use .AddRange() and add everything all at once as a list outside the foreach instead of one by one inside the loop.
List<Sim> _SIMS = new List<Sim>();
foreach (var ToUpdate in SimID)
{
if (ToUpdate.ID == 0)
{
Console.WriteLine("We have found a new MSISDN: " + ToUpdate.MSISDN + " adding it to the model.");
_SIMS.Add(new Sim { Msisdn = ToUpdate.MSISDN} );
}
}
ctx.Sims.AddRange(_SIMS);
I have been trying to get a list of all Workflows that have Offices contained in a certain List by Office Id. I can easily get all of the Workflows that have SingleWorkflowSteps because they have only one Office, but have been unable to understand how I would successfully get those contained in a MultiWorkflowStep. All workflow steps have either a SingleWorkflowStep or a MultiWorkflowStep that contains two or more SingleWorkflowSteps. At the time I designed this, it seemed like a logical way to do this but atlas my LINQ-fu is not as good as I thought it was. Can someone please point me in the right directions. Code listed below:
var OfficesToFind = new List<int> (new int[] { 1,3,5,7,9,10,11,12} );
public class Workflow
{
public Workflow()
{
WorkflowSteps = new List<WorkflowStepBase>();
}
public int Id { get; set; }
public virtual ICollection<WorkflowStepBase> WorkflowSteps { get; set; }
}
public abstract class WorkflowStepBase
{
public int Id { get; set; }
public int StatusId { get; set; }
public virtual Workflow Workflow { get; set; }
public virtual Status Status { get; set; }
}
public class MultiWorkflowStep : WorkflowStepBase
{
public MultiWorkflowStep()
{
ChildSteps = new List<SingleWorkflowStep>();
}
public virtual ICollection<SingleWorkflowStep> ChildSteps { get; set; }
}
public class SingleWorkflowStep : WorkflowStepBase
{
public int? ParentStepId { get; set; }
public int OfficeId { get; set; }
public virtual MultiWorkflowStep ParentStep { get; set; }
public virtual Office Office { get; set; }
}
public class Office
{
public int Id { get; set; }
public string Name { get; set; }
}
public class WorkflowService : IWorkflowService<Workflow>
{
private readonly IRepository<Workflow> _workflowService;
private readonly IRepository<SingleWorkflowStep> _singleStepService;
private readonly IRepository<MultiWorkflowStep> _multiStepService;
public WorkflowService(IUnitOfWork uow)
{
_workflowService = uow.GetRepository<Workflow>();
_singleStepService = uow.GetRepository<SingleWorkflowStep>();
_multiStepSercice = uow.GetRepository<MultiWorkflowStep>();
}
// ~ ------- Other CRUD methods here -------- ~
public IEnumerable<Workflow> GetWorkflowFilter(List<int> statuses, List<int> offices...)
{
var query = _workflowService.GetIQueryable(); // returns an IQueryable of dbset
if(statuses.Any())
{
query = query.Where(q => statuses.Contains(q.StatusId));
}
if(offices.Any())
{
// Get all active single steps and the ones that contain the offices
singleSteps = _singleStepService
.Where(s => s.StatusId == (int)Enumerations.StepStatus.ACTIVE)
.Where(s => offices.Contains(s.OfficeId));
// Get all of the parent Workflows for the singleSteps
var workflows = singleSteps.Select(w => w.Workflow);
// Update the query with the limited scope
query = query.Where(q => q.Workflow.Contains(q));
}
return query.ToList();
}
}
OK, after a good night sleep, being all bright-eyed and bushy-tailed, I figured out my own problem. First the updated code was all wrong. Because each derived WorkflowStep has access to the Workflow and each MultiWorkflowStep contains a list of SingleWorkflowSteps - when I get the list of all SingleWorkflowSteps (which would include all from MultiWorkflowStep(s)), I simply needed to get a list of all of the parent Workflows of the SingleWorkflowSteps. Next I updated my query to limit the Workflows that were contained in the new Workflow list and here is the correct code for the GetWorkflowFilter method:
...
if(offices.Any())
{
// Get all active single steps and the ones that contain the offices
singleSteps = _singleStepService.Where(s => s.StatusId == (int)Enumerations.StepStatus.ACTIVE).Where(s => offices.Contains(s.OfficeId));
// Get all of the parent Workflows for the singleSteps
var workflows = singleSteps.Select(w => w.Workflow);
// Update the query with the limited scope
query = query.Where(q => q.Workflow.Contains(q));
}
return query.ToList();
}
I am working with the EF6 and I am a big fan of the dynamic proxies, which enables lazy loading and change tracking. Anyway I am not happy, that the lazy loading is triggered once the property is accessed instead of loading the data, when the enumerator or the count property is called first. Therefore I tried to diesable the proxys and replace them by custom proxies. It was an easy thing to use a custom object context and overload the CreateObject method. Unfortantly the ObjectMaterialized event cannot replace the entity and I am not able to replace an entity from a query. The creation of the object lies deep in internal classes of the framework.
Has anybody an idea how to use custom proxies? Or how I am able to replace the entities materialized in an object query?
You should .Include the properties you want to fetch so that you avoid an N+1 query problem.
public class User
{
public int Id { get; set; }
public string Name { get; set ;}
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set ; }
public int AuthorId { get; set; }
public virtual User Author { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Note { get; set ;}
public int PostId { get; set; }
public virtual Post Post { get; set; }
public int AuthorId { get; set; }
public virtual User Author { get; set; }
}
public class BlogContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
Then this is BAD in that it'll do tons of queries:
using (var db = new BlogContext())
{
var user = db.Users.Single(u => u.Id=5)); // +1 query
foreach (var post in user.Posts) // N queries
{
var message = String.Format("{0} wrote {1}", user.Name, post.Title);
Console.WriteLine(message);
foreach (var comment in post.Comments) // N * M queries!
{
// and that .Author make N * M MORE!
var message = String.Format("\t{0} commented {1}", comment.Author.Name, comment.Note);
Console.WriteLine(message);
}
}
}
And this is GOOD in that it'll do one query:
using (var db = new BlogContext())
{
var user = db.Users
.Single(u => u.Id=5))
.Include(u => u.Posts) // eliminates the N post queries
.Include(u => u.Posts.Comments) // eliminates the M comment queries
.Include(u => u.Posts.Comments.Author); // eliminates the M comment author queries
foreach (var post in user.Posts) // N queries
{
var message = String.Format("{0} wrote {1}", user.Name, post.Title);
Console.WriteLine(message);
foreach (var comment in post.Comments) // N * M queries!
{
// and that .Author make N * M MORE!
var message = String.Format("\t{0} commented {1}", comment.Author.Name, comment.Note);
Console.WriteLine(message);
}
}
}
I have a MVC3 app with the following model
public class Class1
{
public List<Class2> Class2Data { get; set; }
}
public class Class2
{
public int Id { get; set; }
public IEnumerable<Class3> Class3Data { get; set; }
}
public class Class3
{
public int Id { get set; }
public bool Selected { get; set; }
}
In my razor model
for (var i = 0; i < Model.Class2Data.Count();i++)
{
#Html.HiddenFor(c => c.Class2Data[i].Id)
foreach (var n in Model.Class2Data[i].Class3Data.ToList())
{
#Html.CheckBoxFor(x=> n.Selected);
#Html.HiddenFor(x=> n.Id);
}
}
However, when iam posting this to my controller, the Class3Data count is always 0 when I tick the checkboxes. Any ideas? Thanks
Resolved by changing IEnumerable to List as wasn't binding properly
I have a self referencing model with composite primary key in ASP.NET MVC 3 using code first approach:
public class Area
{
[Key, Column(Order=0)]
public int Id1 { get; set; }
[Key, Column(Order=1)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual Area Parent { get; set; }
}
And I would like to have a controller with create and edit operations that can work with all properties, including the composite Parent (that must be previously added to the database).
I managed to get the create method running, but for editing the complex field doesn't want to update. The input data for the following method successfully parses to the object area that also has area.Parent.Id1 and area.Parent.Id2 set.
Current code that doesn't save changes for modifications to Parent:
[HttpPost]
public ActionResult Edit(Area area)
{
try
{
if (ModelState.IsValid)
{
if (area.Parent != null)
{
area.Parent = db.Areas.Find(area.Parent.Id1, area.Parent.Id2);
if (area.Parent == null)
throw new NotFoundException();
// need to mark it as modified...
}
db.Entry(area).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (NotFoundException)
{
//...
}
return View(area);
}
I found a kind of workaround if anyone needs it. Add this to the model:
[ForeignKey("Parent"), Column(Order = 0)]
public int Parent_Id1 { get; set; }
[ForeignKey("Parent"), Column(Order = 1)]
public int Parent_Id2 { get; set; }
And add this where it says "need to mark it as modified":
area.Parent_Id1 = area.Parent.Id1;
area.Parent_Id2 = area.Parent.Id2;