Better way to handle this code - asp.net-mvc-3

I am working on a MVC3 application with nhibernate and SQL server. Have written a normal method which is re-usable. Please find the below code and let me know a better way to handle it. I have observed to execute this piece of code it is taking a long time.
private void GetParentCompany(IEnumerable<Company> companiesList)
{
foreach (var company in companiesList)
{
long? dunsUltimateParent = company.DUNSUltimateParent;
Company ultimateParent = _companyService.GetCompanyByDUNS(Convert.ToInt64(dunsUltimateParent));
if (ultimateParent != null)
{
company.UltimateParentName = ultimateParent.CompanyName;
company.UltimateCompanyId = ultimateParent.CompanyId;
company.UltimateParentDuns = ultimateParent.DUNS;
}
}
}

Adding an index to your company.DUNS column might help. However consider to introduce a many-to-one relationship from company to (parent) company.
Place a UltimateParent property with type company in the company class. The fields UltimateParentName and UltimateParentDuns would then be redundant and you could simply get company.UltimateParent.Name for example. The mapping of UltimateParent can be done using 'References' in fluent-nhibernate.
References(x => x.UltimateParent);

Related

Recursive linq expressions to get non NULL parent value?

I wrote a simple recursive function to climb up the tree of a table that has ID and PARENTID.
But when I do that I get this error
System.InvalidOperationException: 'The instance of entity type 'InternalOrg' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Is there another way to do this or maybe done in one LINQ expression ?
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
{
if (dcDto.InternalOrgId != null)
{
InternalOrg io = _internalOrgRepo.Get(Convert.ToInt32(dcDto.InternalOrgId));
InternalOrgDto ioDto = new InternalOrgDto
{
Id = io.Id,
Abbreviation = io.Abbreviation,
Code = io.Code,
Description = io.Description
};
return ioDto;
}
else
{
//manually get parent department
Department parentDepartment = _departmentRepo.Get(Convert.ToInt32(dcDto.ParentDepartmentId));
DepartmentChildDto parentDepartmenDto = ObjectMapper.Map<DepartmentChildDto>(parentDepartment);
return GetInternalOrgDto(parentDepartmenDto);
}
}
Is there a way to get a top-level parent from a given child via Linq? Not that I am aware of. You can do it recursively similar to what you have done, though I would recommend simplifying the query to avoid loading entire entities until you get what you want. I'm guessing from your code that only top level parent departments would have an InternalOrg? Otherwise this method would recurse up the parents until it found one. This could be sped up a bit like:
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
{
var internalOrgid = dcDto.InternalOrgId
?? FindInternalOrgid(dcDto.ParentDepartmentId)
?? throw new InternalOrgNotFoundException();
InternalOrgDto ioDto = _context.InternalOrganizations
.Where(x => x.InternalOrgId == internalOrgId.Value)
.Select(x => new InternalOrgDto
{
Id = x.Id,
Abbreviation = x.Abbreviation,
Code = x.Code,
Description = x.Description
}).Single();
return ioDto;
}
private int? FindInternalOrgid(int? departmentId)
{
if (!departmentId.HasValue)
return (int?) null;
var details = _context.Departments
.Where(x => x.DepartmentId == departmentId.Value)
.Select(x => new
{
x.InternalOrgId,
x.ParentDepartmentId
}).Single();
if (details.InternalOrgId.HasValue)
return details.InternalOrgId;
return findInternalOrgId(details.parentDepartmentId);
}
The key considerations here are to avoid repository methods that return entities or sets of entities, especially where you don't need everything about an entity. By leveraging the IQueryable provided by EF through Linq we can project down to just the data we need rather than returning every field. The database server can accommodate this better via indexing and help avoid things like locks. If you are using repositories to enforce low level domain rules or to enable unit testing then the repositories can expose IQueryable<TEntity> rather than IEnumerable<TEntity> or even TEntity to enable projection and other EF Linq goodness.
Another option to consider where I have hierarchal data where the relationships are important and I want to quickly find all related entities to a parent, or get to a specific level, one option is to store a breadcrumb with each record which is updated if that item is ever moved. The benefit is that these kinds of checks become very trivial to do, the risk is that anywhere/anything that can modify data relationships could leave the breadcrumb trail in an invalid state.
For example, if I have a Department ID 22 which belongs to Department 8 which belongs to Department 2 which is a top-level department, 22's breadcrumb trail would be: "2,8". If the breadcrumbs are empty we have a top-level entity. (and no parent Id) We can parse the breadcrumbs using a simple string.Split() operation. This avoids the recursive trips to the DB entirely. Though you may want a maintenance job running behind the scenes to periodically inspect recently modified data to ensure their breadcrumb trails are accurate and alerting you if any get broken. (Either by faulty code or such)

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

Users restrictions for associated data in ASP Membership

I have a site I'm porting to MVC to clean up the code and simplify things. I use the asp membership and profile providers, but I'm wondering if I'm doing this correctly for my situtation. I'm pretty new to MVC, so I wan to get this right in the early stages.
Users are individuals and they are part of larger "institutions" that they either set up or pick at registration. In this case, the institution is a winery. I want the users to be able to view all wines from every winery, but only edit ones that belong to them.
What's the best way to do this? Right now I render the link to the edit field in my index view based on their instution ID and the producer ID. I feel like a data annotation might work better here, but I don't exactly how to implement that for a group of wines. Do I need multiple providers? I use roles to limit the editing, but right now an editor role could manually enter the path of another wine to edit it when that wine doesn't belong to them.
Any pointers here would be awesome. I know I can do it in the controller methods, but I'm looking for the 'right' way to do it. Thanks.
I'm running into the same issue at work right now, and the best proposed solution we have right now is implementing an "ownership" table. You won't be able to solve this using roles.
So basically you have an owner ID, owned object's ID, and the type of objects ID all held together. Lets take an edit request for example. We know that you can only edit the data person X owns, so we have a stored procedure that if a key combination exists in our ownership table where person.ID = owner ID, and item.ID = object ID, and item.TypeID = objectTypeID. If it exists, it goes along performing its edits, otherwise it returns an error.
You can use this scheme to return ownership lists, user validation, and a host of other issues you may come across. You probably won't need the ObjectTypeID if you only have one type's ownership being tracked. Hope this helps!
I figured this out by applying a custom AuthorizeAttribute to the edit, delete, and create actions.
Here is what I ended up doing:
public class ProducerEditAttribute : AuthorizeAttribute
{
private vfContext db = new vfContext();
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
bool bAdmin = filterContext.HttpContext.User.IsInRole("admin");
bool bProdEdit = filterContext.HttpContext.User.IsInRole("producereditor");
bool bProd = filterContext.HttpContext.User.IsInRole("producer");
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (bAdmin)
{
//authorized
}
if (bProdEdit || bProd)
{
//check for current wine belonging to the producer.
Producer currentProd = db.Producers.Find(Profile.GetProfile(filterContext.HttpContext.User.Identity.Name).InstitutionID);
Wine currentWine;
object WineID;
if (filterContext.RouteData.Values.TryGetValue("id", out WineID))
{
currentWine = db.Wines.Find(int.Parse(WineID.ToString()));
if (currentProd.Wines.Contains(currentWine) && bProdEdit)
{
//authorized
}
else if (bProd)
{
var result = new ViewResult();
result.ViewName = "Login.cshtml"; //this can be a property you don't have to hard code it
result.MasterName = "_Layout.cshtml"; //this can also be a property
result.ViewBag.Message = "You do not have permission for add/edit/delete funciontionality. Please request.";
filterContext.Result = result;
}
else
{
var result = new ViewResult();
result.ViewName = "Login.cshtml";
result.MasterName = "_Layout.cshtml";
filterContext.Result = result;
}
}
}
else
{
var result = new ViewResult();
result.ViewName = "Login.cshtml";
result.MasterName = "_Layout.cshtml";
}
}
}
}

Entity Framework Code-First: "The ObjectStateManager cannot track multiple objects with the same key."

I'm running into an issue with Entity Framework code-first in MVC3. I'm hitting this exception:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
This is addressed many times on SO, but I'm having trouble utilizing any of the suggested solutions in my situation.
Here is a code sample:
FestORM.SaleMethod method = new FestORM.SaleMethod
{
Id = 2,
Name = "Test Sale Method"
};
FestContext context = new FestContext();
//everything works without this line:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
context.Entry(method).State = System.Data.EntityState.Modified;
context.SaveChanges();
EDITED to clarify: I am attempting to update an object that already exists in the database.
Everything works fine without the query noted in the code. In my application, my controller is instantiating the context, and that same context is passed to several repositories that are used by the controller--so I am not able to simply use a different context for the initial query operation. I've tried to remove the entity from being tracked in the ObjectStateManager, but I can't seem to get anywhere with that either. I'm trying to figure out a solution that will work for both conditions: sometimes I will be updating an object that is tracked by the ObjectStateManager, and sometimes it will happen to have not been tracked yet.
FWIW, my real repository functions look like this, just like the code above:
public void Update(T entity)
{
//works ONLY when entity is not tracked by ObjectStateManager
_context.Entry(entity).State = System.Data.EntityState.Modified;
}
public void SaveChanges()
{
_context.SaveChanges();
}
Any ideas? I've been fighting this for too long...
The problem is that this query
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
brings one instance of the SaleMethod entity into the context and then this code
context.Entry(method).State = System.Data.EntityState.Modified;
attaches a different instance to the context. Both instances have the same primary key, so EF thinks that you are trying to attach two different entities with the same key to the context. It doesn't know that they are both supposed to be the same entity.
If for some reason you just need to query for the name, but don't want to actually bring the full entity into the context, then you can do this:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name;
If what you are tying to do is update an existing entity and you have values for all mapped properties of that entity, then the simplest thing to do is to not run the query and just use:
context.Entry(method).State = System.Data.EntityState.Modified;
If you don't want to update all properties, possibly because you don't have values for all properties, then querying for the entity and setting properties on it before calling SaveChanges is an acceptable approach. There are several ways to do this depending on your exact requirements. One way is to use the Property method, something like so:
var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();
These blog posts contain some additional information that might be helpful:
http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx
The obvious answer would be that your not actually saving the method object to the database before you call:
//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;
However, I think perhaps this is just a bit a code you left out.
What if you make your entities inherit from an abstract class ie.
public abstract class BaseClass
{
public int Id { get; set; }
}
Then update your Repository to
public class Repository<T> where T : BaseClass
{
.....
public void Update(T entity)
{
_context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
}
}
Also you might want to not set the ID of your SaleMethod and let it be generated by the database. Problem could also be because SaleMethod Object in the database has Id of 2 and then you try to add another SaleMethod object with Id 2.
The error you see stems from trying to add another SaleMethod object with ID of 2 to the ObjectStateManager.

Entity Framework 4 - List<T> Order By based on T's children's property

I have the following code -
public void LoadAllContacts()
{
var db = new ContextDB();
var contacts = db.LocalContacts.ToList();
grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name));
grdItems.DataBind();
}
I'm trying to sort the list of the contacts according to the area name that is contained within each contact. When I tried the above, I get "At least one object must implement IComparable.". Is there an easy way instead of writing a custom IComparer?
Thanks!
try this:
public void LoadAllContacts()
{
var db = new ContextDB();
var contacts = db.LocalContacts.ToList();
grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name).First().Name);
grdItems.DataBind();
}
this will order the contacts by the first area name, after ordering the areas by name.
Hope this helps :)
Edit: fixed error in code. (.First().Name)
I was in a discussion with #AbdouMoumen but in the end I thought I'd provide my own answer :-)
His answer works, but there two performance issues in this code (both in the answer as in the original question).
First, the code loads ALL contacts in the db. This may or may not be a problem, but in general I would recommend NOT to do this. Many modern controls support paging/filtering out of the box, so you'd be better off supplying an not-yet-evaluated IQueryable<T> instead of List<T>. If however you need everything in memory, you should delay the ToList to the last possible moment.
Second, in AbdouMoumen's answer, there is a so-called 'SELECT N+1' problem. Entity Framework will by default use lazy loading to fetch additional properties. I.e. the Areas property will not be fetched from the database until it's accessed. In this case this will happen in the controls 'for loop', while it's ordering the result set by name.
Open up SQL Server Profiler to see what I mean: you will see a SELECT statement for all the contacts, and an additional SELECT statement for each contact that fetches the Areas for that contact.
A much better solution would be the following:
public void LoadAllContacts()
{
using (var db = new ContextDB())
{
// note: no ToList() yet, just defining the query
var contactsQuery = db.LocalContacts
.OrderBy(x => x.Areas
.OrderBy(y => y.Name)
.First().Name);
// fetch all the contacts, correctly ordered in the DB
grdItems.DataSource = contactsQuery.ToList();
grdItems.DataBind();
}
}
Is it one to one relation (Contact->Area)?
if yeah then try the following :
public partial class Contact
{
public string AreaName
{
get
{
if (this.Area != null)
return this.Area.Name;
return string.Empty;
}
}
}
then
grdItems.DataSource = contacts.OrderBy(x => x.AreaName);

Resources