How to create a list of child IDs - asp.net-mvc-3

In my controller I have a method that receives a decimal value (id).
The objective of this method is to recover a list of old revisions from a database table containing work permits. Each record on this table has a WorkPermitID as a primary key and OldRevisionWorkPermitID referencing the ID of the previous version.
I have no problems when collecting the children IDs (old versions), but it raises an exception indicating that LINQ to Entities does not recognize .ToString() method.
What I'm doing wrong? I know that I need to do without converting to string (WorkPermitID is defined as numeric in the database), but I tried several ways with no success.
public ActionResult GetVersions(decimal id){
var model = new PermisosTrabajoModel();
List<string> ChildIDs = new List<string>();
var WP = OtWeb.WorkPermit.Single(q => q.WorkPermitID == id);
while (WP.OldRevisionWorkPermitID != null)
{
var child = WP.OldRevisionWorkPermitID;
ChildIDs.Add(child.ToString());
WP = OtWeb.WorkPermit.Single(q => q.WorkPermitID == child);
}
model.WPs = OtWeb.WorkPermit
.Where(q => q.DeptID == 1
&& ChildIDs.Contains(q.WorkPermitID.ToString())).ToList();
return View (model);
}

Solution1
If both of your fields are decimal... Don't use ToString(), and use a list of decimal
var model = new PermisosTrabajoModel();
var childIDs = new List<decimal>();
var WP = OtWeb.WorkPermit.Single(q => q.WorkPermitID == id);
while (WP.OldRevisionWorkPermitID != null)
{
childIDs.Add(WP.OldRevisionWorkPermitID);
WP = OtWeb.WorkPermit.Single(q => q.WorkPermitID == child);
}
model.WPs = OtWeb.WorkPermit
.Where(q => q.DeptID == 1
&& childIDs.Contains(q.WorkPermitID)).ToList();
Solution2
In linq2entities, you can use SqlFunctions.StringConvert instead of ToString() for a numeric value.
SqlFunctions.StringConvert(q.WorkPermitId)
instead of
q.WorkPermitID.ToString()
for example

Related

How to get all items with the same value in list of lists c# LINQ?

I have a to add a specific requirement in a piece of code already implemented.
The data structure is something of this sort:
public class Module
{
public string Type;
public string ID;
public List<Point> Points = new List<Point>();
}
public class Point
{
public string Type;
public string Location;
public string Connection;
}
Originally LINQ was used to return all modules which certain characteristics
List<Module> miList = Modules.Where(m => m.Type != null
&& m.ID == "A"
&& m.Points.Where(t => t.Connection == ""
&& SimilarPoint(t.Type, x).ToList())
.Count() > 0)
.ToList();
with x an input to the function. The new requirement dictates that the modules returned shall all have points with Connection equal to "" and the same value in the Location field.
It seemed to me that the SelectMany could be used to this end, but I am not getting what I expected.
How should the function above be modified?
Thanks in advance
Not exactly sure what the SimilarPoint(t.Type, x) does here.
May be you should try something like this and find out if it works for you -
var resultSet = Modules.Where(m => !string.IsNullOrEmpty(m.Type) && m.ID.Equals("A"))
.Select(n =>
new Module {
Type=n.Type,
ID=n.ID,
Points= n.Points.Where(p => String.IsNullOrEmpty(p.Connection) && String.IsNullOrEmpty(p.Location)).ToList()
})
.ToList();
You said all the returned modules have the same Location, but that doesn't explain how you select which Location so I arbitrarily picked the first matching module's location:
var miQuery1 = Modules.Where(m => m.Type != null
&& m.ID == "A"
&& m.Points.Where(t => t.Connection == ""
&& SimilarPoint(t.Type, x).ToList()).Count() > 0)
.Where(m => m.Points.All(p => p.Connection == ""));
var miQuery2 = miQuery1.Where(m => m.Location == miQuery1.First().Location);
List<Module> miList = miQuery2.ToList();

Deep LINQ projections, how?

Let's say I have a model created with EF 4.0
User
Roles
Permissions
Each entity has a DeleteDate property.
I want to get a specific user (with Name =...) and have the tree filled with items where DeletedDate == null..
This must be done with anonymous type projection as result, but I don't know how to accomplish this with a hierachy deeper than 2..
This is what I already have:
public MyProjection MyCall(string givenName)
{
var result = from s in context.Users
where (s.Name == givenName &&
s.DeletedDate == null)
select new
{
s,
roles = from r in s.Roles
where r.DeletedDate == null
select r
};
var outcome = result.FirstOrDefault();
if (outcome != null)
{
var myProjection = new MyProjection()
{
User = outcome.s,
Roles = outcome.roles
};
return myProjection;
}
return null;
}
Depending on your structure you could do something like this:
var result = m.Users.Where(u => u.DeletedDate == null)
.Select( u => new
{
u,
roles = u.Roles.Where(r => r.DeletedDate == null)
.Select(r => new
{
r,
permissions = r.Permissions.Where(p => p.DeletedDate == null)
})
}).FirstOrDefault(item => item.u.Name == givenName);
If you retrieve with the following:
var result = from s in MyUsers
where s.DeletedDate == null
select new aUser{
Roles = (from r in s.Roles
where r.DeletedDate == null
select r).ToList()
};
And then create a TreeView:
TreeView treeView = new TreeView();
Then set the ItemsSource of the TreeView to the IEnumerable:
treeView.ItemsSource = result;
Then build a HierarchicalDataTemplate in your TreeView to represent your Lists (similar to this or for more in depth this), then voila!

EF single entity problem

I need to return a single instance of my viewmodel class from my repository in order to feed this into a strongly-typed view
In my repository, this works fine for a collection of viewmodel instances:
IEnumerable<PAWeb.Domain.Entities.Section> ISectionsRepository.GetSectionsByArea(int AreaId)
{
var _sections = from s in DataContext.Sections where s.AreaId == AreaId orderby s.Ordinal ascending select s;
return _sections.Select(x => new PAWeb.Domain.Entities.Section()
{
SectionId = x.SectionId,
Title = x.Title,
UrlTitle = x.UrlTitle,
NavTitle = x.NavTitle,
AreaId = x.AreaId,
Ordinal = x.Ordinal
}
);
}
But when I attempt to obtain a single entity, like this:
public PAWeb.Domain.Entities.Section GetSection(int SectionId)
{
var _section = from s in DataContext.Sections where s.SectionId == SectionId select s;
return _section.Select(x => new PAWeb.Domain.Entities.Section()
{
SectionId = x.SectionId,
Title = x.Title,
UrlTitle = x.UrlTitle,
NavTitle = x.NavTitle,
AreaId = x.AreaId,
Ordinal = x.Ordinal
}
);
}
I get
Error 1 Cannot implicitly convert type
'System.Linq.IQueryable<PAWeb.Domain.Entities.Section>' to
'PAWeb.Domain.Entities.Section'. An explicit conversion exists
(are you missing a cast?)"
This has got to be simple, but I'm new to c#, and I can't figure out the casting. I tried (PAWeb.Domain.Entities.Section) in various places, but no success. Can anyone help??
Your query is returning an IQueryable, which could have several items. For example, think of the difference between an Array or List of objects and a single object. It doesn't know how to convert the List to a single object, which one should it take? The first? The last?
You need to tell it specifically to only take one item.
e.g.
public PAWeb.Domain.Entities.Section GetSection(int SectionId)
{
var _section = from s in DataContext.Sections where s.SectionId == SectionId select s;
return _section.Select(x => new PAWeb.Domain.Entities.Section()
{
SectionId = x.SectionId,
Title = x.Title,
UrlTitle = x.UrlTitle,
NavTitle = x.NavTitle,
AreaId = x.AreaId,
Ordinal = x.Ordinal
}
).FirstOrDefault();
}
This will either return the first item, or null if there are no items that match your query. In your case that won't happen unless the table is empty since you don't have a where clause.

Iterating tables in a context and the properties of those tables

I'm iterating the tables of a context and then the properties of those tables to eager load all columns in a context. I received some help via another question, but I don't seem to be able to figure out how to iterate the column properties of the actual table.
Final working code:
public static void DisableLazyLoading(this DataContext context)
{
DataLoadOptions options = new DataLoadOptions();
var contextTables = context.GetType().GetProperties().Where(n => n.PropertyType.Name == "Table`1");
foreach (var contextTable in contextTables)
{
var tableType = contextTable.GetValue(context, null).GetType().GetGenericArguments()[0];
var tableProperties = tableType.GetProperties().Where(n => n.PropertyType.Name != "EntitySet`1");
foreach (var tableProperty in tableProperties)
{
ParameterExpression paramExp = Expression.Parameter(tableType, "s");
Expression expr = Expression.Property(paramExp, tableProperty.Name);
options.LoadWith(Expression.Lambda(expr, paramExp));
}
}
context.LoadOptions = options;
}
You're only getting the ProperInfos. You need to get the values from the PropertyInfos:
var tablePropertInfos = context.GetType().GetProperties().Where(
n => n.PropertyType.Name == "Table`1");
foreach (var tablePropertyInfo in tablePropertInfos)
{
// Get the actual table
var table = tablePropertyInfo.GetValue(context, null);
// Do the same for the actual table properties
}
Once you have the PropertyInfo class, you need to get the value using the GetValue method.

Linq to SQL Associations with fixed fields

Linq to SQL, in the dbml designer (or otherwise)
I have 3 tables:
Orders, Deliveries and EmailTemplates.
Orders have many Deliveries, and Orders and Deliveries have a status (int) field.
EmailTemplates have a status they apply to and a bool IsForDeliveries field.
I have Linq to sql associations for Order->EmailTemplate on order.status == emailTemplate.status, but I want to add a condition on the association such that emailTemplate.IsForDeliveries == false. Is this possible, or do I just have to remember to check this condition whenever I access order.EmailTemplates?
Edit
AssociateWith is problematic because I also need the counterpart Delivery<->EmailTemplate association which only shows templates with e.is_for_delivery == true.
Adding a property to the class is problematic because I'd like this to be translatable to SQL.
You can use DataLoadOptions.AssociateWith() for this or just create an additional property on the class. Since the Order class is a partial, you could just create a method in a partial beside it adding this:
public IEnumerable<EmailTemplate> DeliveryEmailTemplates {
get { return EmailTemplates.Where(e => e.IsForDeliveries == false); }
}
or alternatively, when creating the datacontext:
var dc = new DataContext();
var dlo = new DataLoadOptions();
dlo.AssociateWith<EmailTemplates>(o => o.EmailTemplates.Where(e => e.IsForDeliveries == false));
dc.LoadOptions = dlo;
var orders = from o in DB.Orders
where o.Id == 5
select o;
foreach(var o in orders) {
//o.EmailTemplates will contain only IsForDeliveries==false...
}
If you fetch your DataContext in a static way you can add this every time, for example I add a method in a partial class of the DataContext called .New:
public static DataContext New {
get {
var dc = new DataContext(MyConnectionString);
var dlo = new DataLoadOptions();
dlo.AssociateWith<EmailTemplates>(o => o.EmailTemplates.Where(e => e.IsForDeliveries == false));
dc.LoadOptions = dlo;
return dc;
}
}
Then use:
var DB = DataContext.New;
var orders = from o in DB.Orders
where o.Id == 5
select o;

Resources