LINQ to SQL in ASP.NET MVC and Repository pattern - asp.net-mvc

I'm trying to follow a tutorial from the Asp.NET MVC website which uses LINQ to Entities but I decided to use LINQ to SQL instead. I'm at the point where a new table is created called Groups which has a relationship to the Contacts table. Esentially it's a One to Many relationship where a Group can have many contacts and a Contact can only have 1 Group. Please see below the example code with CRUD operations.
I'm not sure how to implement this in LINQ to SQL. For example, how do you do this in LINQ to SQL:
return _entities.GroupSet.Include("Contacts").FirstOrDefault();
Are you supposed to do a JOIN for the two tables or is there another way?
Example CODE:
using System.Collections.Generic;
using System.Linq;
using System;
namespace ContactManager.Models
{
public class EntityContactManagerRepository : ContactManager.Models.IContactManagerRepository
{
private ContactManagerDBEntities _entities = new ContactManagerDBEntities();
// Contact methods
public Contact GetContact(int id)
{
return (from c in _entities.ContactSet.Include("Group")
where c.Id == id
select c).FirstOrDefault();
}
public Contact CreateContact(int groupId, Contact contactToCreate)
{
// Associate group with contact
contactToCreate.Group = GetGroup(groupId);
// Save new contact
_entities.AddToContactSet(contactToCreate);
_entities.SaveChanges();
return contactToCreate;
}
public Contact EditContact(int groupId, Contact contactToEdit)
{
// Get original contact
var originalContact = GetContact(contactToEdit.Id);
// Update with new group
originalContact.Group = GetGroup(groupId);
// Save changes
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
_entities.SaveChanges();
return contactToEdit;
}
public void DeleteContact(Contact contactToDelete)
{
var originalContact = GetContact(contactToDelete.Id);
_entities.DeleteObject(originalContact);
_entities.SaveChanges();
}
public Group CreateGroup(Group groupToCreate)
{
_entities.AddToGroupSet(groupToCreate);
_entities.SaveChanges();
return groupToCreate;
}
// Group Methods
public IEnumerable<Group> ListGroups()
{
return _entities.GroupSet.ToList();
}
public Group GetFirstGroup()
{
return _entities.GroupSet.Include("Contacts").FirstOrDefault();
}
public Group GetGroup(int id)
{
return (from g in _entities.GroupSet.Include("Contacts")
where g.Id == id
select g).FirstOrDefault();
}
public void DeleteGroup(Group groupToDelete)
{
var originalGroup = GetGroup(groupToDelete.Id);
_entities.DeleteObject(originalGroup);
_entities.SaveChanges();
}
}
}

You need to specify some DataLoadOptions to create the join for you:
So to do this, you have to create a DataContext for each type of query with the correct DataLoadOptions:
var db = new WhateverDbDataContext();
DataLoadOptions options = new DataLoadOptions();
db.LoadOptions = options;
options.LoadWith(x => x.Contacts);
return db.SomeTable.FirstorDefault();

Linq to sql does not support the Include method. If you don't care if the relationship is lazy loaded, then you don't have to do anything. If you want it to be eager loaded, then you have use the more convoluted DataLoadOptions.
See this article:
http://blog.stevensanderson.com/2007/12/02/linq-to-sql-lazy-and-eager-loading-hiccups/

Related

Only getting one child observable when I retrieve complete objects graph

I have a large object graph that I want to return to the client (an overview of the entire model) so I decided to send it back as one big object (I'm returning it as the object in question.)
In Breeze however I'm only getting the first object in each dependent object. So for example I have a 'policy' object with two 'vehicle' objects. I only see one vehicle (even when I put a breakpoint at var p = data.results[0]. The json coming back from the call shows two vehicles but breeze is catching only one.
What can I do to get the other ones? Here's the call I'm making:
var getPolicyByPolicyNumber = function (lob, policynumber, policyObservable) {
var params = {};
params['lOBCd'] = lob;
params['policyNumber'] = policynumber;
var query = EntityQuery.from('PolicyInquiry')
.withParameters(params);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var p = data.results[0];
p.completeTree(true);
return policyObservable(p);
}
};
And in my breeze controller I have the following:
[System.Web.Http.HttpGet]
public Policy PolicyInquiry(string lOBCd, string policyNumber)
{
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return policy;
}
And here's an abbreviated model showing policy and vehicle:
public class Policy
{
public int Id { get; set; }
public string PolicyNumber { get; set; }
[InverseProperty("Policy")]
public ICollection<Vehicle> Vehicles { get; set; }
// other fields removed
}
public class Vehicle
{
public int Id {get; set}
public string ItemId { get; set; }
// other fields removed
//Foreign Keys
public int PolicyId { get; set; }
[IgnoreDataMember]
[ForeignKey("PolicyId")]
[Required]
public virtual Policy Policy { get; set; }
}
Now that I see your model I think I see the issue.
Breeze does not automatically resolve the entity graph on a query. i.e. if you retrieve a Policy, Breeze only returns the Policy instance itself without any navigation properties resolved. This is by design, so that a single query doesn't bring down the entire entity graph.
If you want the values of any navigation properties you have three options, the third of which is probably your best bet. I've taken some liberties in simplifying your model for the purposes of explanation. These all assume that the "Policy" type is actually defined as a Breeze entity type, i.e. has a metadata definition in the Breeze metadataStore.
1) Use an client side EntityQuery.expand clause
var query = EntityQuery.from('Policy')
.expand("Vehicles")
.withParameters(params);
2) Use a server side Include clause
[System.Web.Http.HttpGet]
public IEnumberable<Policy>(string lOBCd, string policyNumber) {
return _contextProvider.Context.Policies.Where(....).Include("Vehicles");
}
3) Use a anonymous result, that contains two known entity types.
[System.Web.Http.HttpGet]
public Object PolicyInquiry(string lOBCd, string policyNumber) {
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return new {
Policy = policy,
Vehicles = policy.GetVehicles() // insure that they are actually resolved on the server)
}
return policy;
}
More info here: Navigation Properties
I hope this is clear enough.
Sorry, but without seeing the underlying implementation of "policy" and its metadata, it's hard to tell what's going on. But I can make a general suggestion.
1) If you want to return an aggregate object and have Breeze recognize it's constituent parts, the recommended mechanism is to create a projection and return that. i.e. something like
public IQueryable<Object> CompanyInfoAndOrders() {
return ContextProvider.Context.Customers.Select(c => new { Customer = c, Orders = c.Orders });
}
In this example, providing that Breeze has metadata for the Customer and Order types,
Breeze will deconstruct the result and add the Customer and its orders to the EntityManager, and return a collection of json objects each with a "Customer" and Orders property. The Customer and individual Orders will each have been "adapted" to the current model library as well (i.e. knockout, backbone, or backingStore - for Angular).

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.

How to move data access code from Controller to Repository

I have a Controller which returns a ViewModel to a View and it works just fine. I want to migrate to a Repository pattern but am having trouble getting the correct syntax in the repository. I have created the repository and the interface to it.
public interface IShippingRepository
{
IQueryable<ShippingCommHdr> All { get; }
IQueryable<ShippingCommHdr> AllIncluding(params Expression<Func<ShippingCommHdr, object>>[] includeProperties);
void InsertOrUpdate(ShippingCommHdr shippingcommhdr);
void Delete(int id);
void Save();
}
Here is the code form my Controller that I want to move to the repository:
public ViewResult ShippingSummary()
{
CPLinkEntities context = new CPLinkEntities();
var shipments =
from h in context.ShippingCommHdrs
where (h.CompletedDate == null)
join
e in context.vHr_Employees on h.CreatedBy equals e.ID
join
s in context.Shippers on h.ShipperID equals s.ShipperID
join
r in context.vAaiomsSites on h.ShipToSiteID equals r.SiteID
join
c in context.vHr_Employees on h.CreatedBy equals c.ID
join
p in context.vHr_Employees on h.FromSitePOC equals p.ID
select new
{
h.ID,
ShippedToSite = r.SiteName,
h.DateShipped,
h.EstDeliveryDate,
h.TrackingNo,
h.HeaderComments,
h.ShippingCommLI.Count,
s.Shipper,
CreatedBy = c.LastName,
FromSitePoc = p.LastName
};
var model = new List<ShippingSummaryVM>();
foreach (var h in shipments)
{
var viewModel = new ShippingSummaryVM
{
ID = h.ID,
ShippedToSite = h.ShippedToSite,
DateShipped = h.DateShipped,
EstDeliveryDate = h.EstDeliveryDate,
TrackingNo = h.TrackingNo,
FromSitePOC = h.FromSitePoc,
Shipper = h.Shipper,
HeaderComments = h.HeaderComments,
NumOrders = h.Count,
CreatedBy = h.CreatedBy,
};
model.Add(viewModel);
}
return View(model);
}
If I could get this one Controller/Repository to work, I can then migrate all the others over fairly quickly. thanks for any assistance
I'd start by adding a method definition to the repository interface for the query you need to execute. The repository can give this query a meaningful name:
public interface IShippingRepository
{
IQueryable<Shipment> GetShipments()
// ...
}
In the controller you'll need an instance of the repository. You can inject it into a constructor, or create one in a constructor, but either way the repository will need to talk to the CPLinkEntities context behind the scenes. You'll need to pass a context into the repository for the repository to work with.
public class SomeController : Controller
{
IShippingRepository _shippingRepository;
public SomeController()
{
_shippingRepository = new ShippingRepository(new CPLinkEntities());
}
public ViewResult ShippingSummary()
{
var shipments = _shippingRepository.GetShipments();
// ....
}
}
A concrete repository definition might look like the following.
public class ShippingRepository : IShippingRepository
{
CPLinkEntities _entities;
ShippingRepository (CPLinkEntities entities)
{
_entites = entities;
}
public IQueryable<Shipment> GetShipments()
{
return from ship in _entities.Ships join ... join ... select
}
}
Your controller method basically has 2 responsibilities
Run a Query
Map the results of the query into a view model
You can put that query into a repository, and then you could use an auto-mapper tool like AutoMapper or ValueInjecter to help you map the results of your query to a view model.
Your resulting controller method would simply call the repository to get a list of CPLinkEntities. Your controller method could then take those entities and then call the automapper to give you a list of ShippingSummaryVM's. I've left some implementation details, but this should give you a high level understanding of how to achieve what you are asking.
Option A: Have a stronger domain model. Your repository would responsible for loading root level domain objects and you let the underlying OR/M handle object traversal. Your controller would call a method on shipment to find shipments that are not yet completed. You'd get back a shipment object and could traverse to the related entities to get site name and other details you need for your VM
Option B: Have repositories that return all for each entity, and then do the join in a business or service layer. Entity Framework won't load all even if you say ShippingRepository.All. It only loads at the last responsible moment (when you need a materialized result). So you could have a business method that joins the "All" on each entity and filters based on completed date then returns back the result.
Option A is better but might be a lot more work.

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;
}

Innerjoin in Linq-to-sql for this in asp.net mvc?

I use asp.net mvc... How to write an inner join in linq-to-sql for this sql query
select M.Mat_id,M.Mat_Name,T.Name as Measurement,M.Mat_Type as Description
from Material as M inner join MeasurementTypes as T
on M.MeasurementTypeId = T.Id where M.Is_Deleted=0
And my repository class has this,
public class ConstructionRepository
{
private CRDataContext db = new CRDataContext();
public IQueryable<Material> FindAllMaterials()
{
return db.Materials;
}
}
My result db.Materials is the table data.. I dont want that i want to innerjoin with another table and show the data....
public class ConstructionRepository
{
private CRDataContext db = new CRDataContext();
public IQueryable<Material> FindAllMaterials()
{
//inner join query
}
}
Any suggestions...
This will return anonymous types with the details you've specified in your SQL query but you may prefer to simply return the material objects themselves.
from material in db.Materials
from measurementType in material.MeasurementTypes
where material.Is_Deleted = false
select new {
material..Mat_id,
material.Mat_Name,
Measurement = measurementType.Name,
Description = material.Mat_Type
}

Resources