Choose type in foreach loop - c#

in my program, I use 4 stored procedures, depending on fields that were selected in the form.
ctx = new DataClassesDataContext();
items = (from prjs in ctx.sp_Select_Stuknummers(prjnr) select prjs).ToList();
or
ctx = new DataClassesDataContext();
items = (from prjs in ctx.sp_Select_Stuknummers_Scanner(prjnr, scanner) select prjs).ToList();
and so on ...
I use LINQ to SQL, and for each of these 4 queries, I have a different result class:
sp_Select_StuknummersResult
sp_Select_Stuknummers_ScannerResult
sp_Select_Stuknummers_Scanner_WPSResult
sp_Select_Stuknummers_StuknummerResult
they all have the same fields and definitions.
Now, when I iterate the result:
foreach (sp_Select_StuknummersResult x in items)
{
WPSitems ding = new WPSitems();
ding.ID = x.ID;
ding.Naam = x.Naam;
......
}
I need to pass the type to use (in this example: sp_Select_StuknummersResult )
Is there a way to either
A. easily convert to a new type, so that one can be used every time
or
B. dynamically set the type in the foreach loop ?
maybe there is even a C that I am not aware of...
Any help is highly appreciated !

By default, L2S auto-generates a separate type for each individual stored procedure. However, you can easily change that return type in the Linq-2-Sql Designer.
In the designer, click on the stored procedure. In the Properties window, there's an entry 'Return Type': change it from 'Auto-generated type' to the type you want.
If the stored procedure returns rows from an already mapped table, select that type in the drop-down. If not, you can manually add a class to the designer and configure that type to be returned from the stored procedure.

You can do this using generics/reflection.
public static WPSitems MapObjectWithIdenticalProperties<T>(T itemOfUnknownType) where T : new()
{
var inputType = typeof(T);
var outputType = typeof(WPSitems);
var outputItem = new WPSitems();
foreach (var inputProperty in inputType.GetProperties())
{
var matchingOutputProperty = outputType.GetProperties().FirstOrDefault(x => x.Name == inputProperty.Name);
if(matchingOutputProperty != null)
matchingOutputProperty.SetValue(outputItem, inputProperty.GetValue(itemOfUnknownType));
}
return outputItem;
}
The function above can be called like this:
var items = GetYourDataThatCanBeDifferentTypes();
var mappedItems = new List<WPSitems>();
foreach(var item in items)
mappedItems.add(MapObjectWithIdenticalProperties(item));

If I understand your problem correctly this might help you out:
1.) Create an interface for the result classes (e.g.) I_SP:
sp_Select_StuknummersResult
sp_Select_Stuknummers_ScannerResult
sp_Select_Stuknummers_Scanner_WPSResult
sp_Select_Stuknummers_StuknummerResult
add common variables and methods that you need into that interface.
2.) Make sure your items is List<I_SP>
3.) Create a generic method for your foreach loop
public void MyMethod<T>(List<I_SP> items) where T:I_SP {
foreach (var x in items.OfType<T>)
{
WPSitems ding = new WPSitems();
ding.ID = x.ID;
ding.Naam = x.Naam;
......
}
}
4.) Then just call this like this:
MyMethod<*sp_Select_StuknummersResult result class*>(items);
Hope it's clear enough.

Related

getting properties of dynamic type c#

Consider the following code:
foreach (Type formType in allFormsToLoopThrough)
{
var nonPriorityForm = _context.Query(formType);
foreach (var nonpriority in nonPriorityForm)
{
var name = nonpriority.GetType().Name;
MyWorkAssignmentDTO form = new MyWorkAssignmentDTO
{
FormName = formType.Name
Id = nonpriority.Id
};
}
}
This snippet is looping thought a list of types.
Each type is taken from the list and passed to a Query function that returns an IQueryable - basically a list of records in a given table in a database that matches the type.
Then for each of the record sets that come back, I want to loop through those and from each create a new instance of MyWorkAssignmentDTO. I am only interested in a form name (which I can get from formType) but I cannot get nonpriority.Id
I know for sure that every nonpriority will have an Id once it is resolved in the loop.
What I can't do is implement this to work at run time.
Can anyone help?
I was able to use the dynamic keyword instead of var. While I lose compile time validation, this gets me over the line when I know for sure there will be an Id.
dynamic nonPriorityForm = _context.Query(formType);

Is it possible to remove columns in select query

As the question suggests I want to remove the columns from a select query where in that column are empty.
var query = from a in ...
select new
{
A =(decimal?)null,
B =(decimal?)null,
C = a.Amount1
};
var query2 = from b in ...
select new
{
A = b.Amount2,
B = b.Amount3,
C = (decimal?)null
};
var query3 = query.Concat(query2);
Output:
query3=[0]{A=null, B=null, C=100.00}
[1]{A=100.00, B=50.25, C=null}
Expected Result:
query3=[0]{C=100.00}
[1]{A=100.00, B=50.25}
You can't do this. The result set has to contain items of the same type and even if fields are null they still have to be there.
You could not show them in your UI, but exactly how you do that will depend on the UI.
You can't. A class has a predefined set of fields (not to speak about ExpandoObject which has some compiler tricks going on). This is the same for anonymous type, which you use.
You can't just hide or remove fields which are not filled. What if you iterate over the instances and try to retrieve item.C, which was null and thus removed? That would normally give you a compiler error. How would .NET resolve that?
The only other thing you can do is put in two different types in your list (a list of objects, so untyped), a very bad idea in my opinion. Keep it like this. You could add an indicator which type the row is, to be able to test it easily.
So:
select new
{
Type = "A", // or "B"
A =(decimal?)null,
B =(decimal?)null,
C = a.Amount1
};
A LINQ query typically outputs objects of a common type, so each object has to have the same base type (and the columns associated. The only way to concatenate objects of different types is to cast them to object:
var query = from a in ...
select new
{
C = a.Amount1
};
var query2 = from b in ...
select new
{
A = b.Amount2,
B = b.Amount3,
};
var query3 = query.Cast<object>().Concat(query2.Cast<object>());
but since they're anonymous you'll have to use dynamic to access them since you won;t be able to cast back to the original type, so you'll end up with something like this:
Console.WriteLine(((dynamic)(query3[0])).C);
Console.WriteLine(((dynamic)(query3[1])).A);
or at best:
dynamic list = query3.ToList();
Console.WriteLine(list[0].C);
Console.WriteLine(list[1].A);
but in any case you'll lose compile-time type safety.

Attempting to obtain properties / fields from an (anonymous class) object linq is creating

I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class

How to return Generic.List<Anonymoustype> from a function in C#

ASP.NET 3.5 C#
I am joining two tables using Linq.
Table names are MapAssets and ExitPoint.
In Database they are related with 'has a relationship'
I am writing a function in my BLL to return the joined table
public List<ExitPoints> GetExitPointDetailsByProjectID(int iProjectID)
{
ctx = new CoreDBDataContext();
var exitPointDetails = from ma in ctx.MapAssets
join ep in ctx.ExitPoints
on ma.MapAssetID equals ep.MapAssetID
where ma.ProjectID == iProjectID
select new
{
//would like to have data from both tables here
ctx.MapAssets,
ctx.ExitPoints
};
return exitPointDetails.ToList();
}
This obviuosly doesn't work. And I dont know what to return at all.
All constraint I have for the return is to be able to be bound to a gridview.
is this the correct way? Or else whats the correct way?
You can't, or better, the only way is to return them boxed in a List of object, but this hugely complicates things, because you can't cast them to any type (of course it's anonymous) and you can only access their properties through reflection....
In cases like that, I'd highly suggest you to create a custom class.
EDIT:
On a side note...
If you were using .net 4, things would be easier because you could returns dynamic Type instead of object (look at this link to see dynamic's simplifications), but I'd prefer to create a custom class anyway.
Have a look at how to return anonymous types from Method.
http://forums.asp.net/t/1387455.aspx.
Copying the code from the link.
object ReturnAnonymous()
{
return new { Name="Faisal", City="Chakwal" };
}
// Application entry-point
void Main()
{
object o = ReturnAnonymous();
// This call to 'Cast' method converts first parameter (object) to the
// same type as the type of second parameter - which is in this case
// anonymous type with 'Name' and 'City' properties
var typed = Cast(o, new { Name="", City="" });
Console.WriteLine("Name={0}, City={1}", typed.Name, typed.City);
}
// Cast method - thanks to type inference when calling methods it
// is possible to cast object to type without knowing the type name
T Cast<T>(object obj, T type)
{
return (T)obj;
}
You can use the method mentioned below to return List and
List<object> lstAnonymousTypes = GetExitPointDetailsByProjectID(1);
foreach(object o in lstAnonymousTypes)
{
//Change it accordingly
var typed = Cast(o, new { new MapAssets() , new ExitPoints() });
}
Hope this helps not tried.
You can't return an anonymous type, you can only use an anonymous type in the scope of the method it's in. You could need to create a new class with MapAssets/ExitPoints properties and select a new instance of that class.
You are trying to return List ExitPoints and List of MapAssets which is not possible because you are getting the output from both tables ie ExitPoints and MapAssets. And it is also not possible to return an anonymous type. So in order to retrun the query create a class name ExMapClass with properties that you need as output of the queries. Now after executing the linq query which you have written iterate it ie
create list of newly created class
list newclass = new list ();
foreach( var result in ctx )
{
instantiate the created class
obj.Property1 = var.MapAssets;
obj.Property2 = var.ExitPoints;
newclass.add(obj);
}
now retrun the list of newlycreated class.
hope you got it.
Do you have to bind to this object after you've created it? If not then you can create an "persistent AnonymousType" class that stores the values in a dictionary and returns the property values with a method like:
string lastName AnonType.GetValue<string>("LastName");
int age AnonType.GetValue<int>("Age");
Here is a link to an excellent example. The author also has an example where he creates the "AnonymousType" from a datatable.
I have worked on a variation of this where I provide the ability to query a list of "AnonymousType" with the following syntax:
// Here's the query
var dept13 = anonAgents.AsQueryable()
.Where(x => x.Has("Department", Compare.Equal, 13);
// Here is how the List is constructed
private static AnonymousType ProvisionAgent(string name, int department)
{
return AnonymousType.Create(new
{
Name = name,
Department = department
});
}
private List<AnonymousType> CreateAnonAgentList()
{
var anonAgents = new List<AnonymousType>();
// Dave and Cal are in Department 13
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Dan Jacobs", 13, 44)));
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Calvin Jones", 13, 60)));
// Leasing = Dept 45
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Stanley Schmidt", 45, 36)));
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Jeff Piper", 45, 32)));
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Stewart Blum", 45, 41)));
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Stuart Green", 45, 38)));
// HR = Dept 21
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Brian Perth", 21, 25)));
anonAgents.Add(AnonymousType.Create(CreateAgentAnonType("Katherine McDonnel", 21, 23)));
return anonAgents;
}
Just use and ArrayList
public static ArrayList GetMembersItems(string ProjectGuid)
{
ArrayList items = new ArrayList();
items.AddRange(yourVariable
.Where(p => p.yourProperty == something)
.ToList());
return items;
}
wont this work ?
ctx = new CoreDBDataContext();
var exitPointDetails = from ma in ctx.MapAssets
join ep in ctx.ExitPoints
on ma.MapAssetID equals ep.MapAssetID
where ma.ProjectID == iProjectID
select Tuple.Create(ma, ep);
return exitPointDetails.ToList();

LINQ - Add property to results

Is there a way to add a property to the objects of a Linq query result other than the following?
var query = from x in db.Courses
select new
{
x.OldProperty1,
x.OldProperty2,
x.OldProperty3,
NewProperty = true
};
I want to do this without listing out all of the current properties of my object. There are many properties, and I don't want to have to update this code whenever I may change my class.
I am still learning with LINQ and I appreciate your suggestions.
Add it with partial classes:
public partial class Courses
{
public String NewProperty { get; set; }
}
Then you can assign it after you've created the object.
I suppose you could return a new object composed of the new property and the selected object, like this:
var query = from x in db.Courses
select new
{
Course = x,
NewProperty = true
};
eking's answer will be the most straightforward approach.
If that doesn't work for you (because you need to pass the results around or whatever), and assuming the class you're dealing with already defines the property you want to set, you could create a copy constructor or factory method that takes an existing instance plus the value of the property you want to set:
var query = from x in db.Courses
select new Course(x, valueOfNewProperty);
Alternatively, if Course doesn't define the property, you could subclass it and use the same approach:
var query = from x in db.Courses
select new CourseWithExtraProperty(x, valueOfNewProperty);
(obviously, pick a better name for your subclass)
Again, though, unless you really need to do this, stick with eking's solution.
ServiceStack has a built-in way to handle this with the PopulateWith method.
Here's a code example.
foreach (var item in results)
{
var test1 = new ItemDto().PopulateWith(item);
test1.extraField1 = "extra";
response.Add(test1);
}`
And if you're not using ServiceStack, you can always use AutoMapper.
CreateMap<Foo, Bar>().ForMember(x => x.ExtraBarProperty, opt => opt.Ignore());
If you are looking to dynamically add a property to an object this could be a solution.
This is what has worked for me, I also had a concern and it was what happened with those domain objects that had many properties, the maintainability for any changes in the object was absurd, I managed to build an implementation with LINQ - ExpandObject - Reflection, which helped to keep my object dynamic and only add the additional properties that my view logic required.
var expandedModel = db.Courses.Select(x =>
{
dynamic expandObject = new ExpandoObject();
expandObject.NewProperty= $"PropertyValue";
foreach (var property in x.GetType().GetProperties())
{
((IDictionary<string, object>)expandObject).Add(property.Name, property.GetValue(x));
}
return expandObject;
}).ToList();

Categories

Resources