I'm using a repository for my models like this
public IEnumerable<object> GetDetailList(int userId, int page, int rp, string sortname, string sortorder, ref int num, ref int numpg)
{
var details = (from access in context.erp_sec_companyaccesses
join company in context.erp_maint_companies on Convert.ToInt32(access.company_id) equals company.company_id
where access.user_id == userId
select new
{
pkey = access.company_access_id,
company_access_id = access.company_access_id,
company_code = company.company_code,
company_name = company.company_name,
company_id = company.company_id
});
num = details.Count();
numpg = (int)Math.Ceiling((float)(num / rp));
details = details.OrderBy(sortname+" "+sortorder).Skip(page * rp).Take(rp);
return details;
}
But I'm struggling to upcast the IEnumerable<object> returned to the controller. Is there any alternative than this ?
Update : I give up up-casting, I'll send a typed object instead of anonymous, thanks everybody
You should not be passing around anonymous objects. I have tried doing it yesterday and did not find an easy solution. So the best thing to do is create another class for your data.
Here is the answer from Skeet: Why should anonymous types cannot be passed around methods?
I'm guessing that you probably want to create a concrete type e.g. Details and return an IEnumerable<Details> from your function rather than an IEnumerable<object>.
I'm assuming this because it appears you probably want to access the fields of your anonymous type created inside the function elsewhere in your code.
Why not use Generics and pass the Object type you want to your method and returning this?
Related
At first,I do not use dynamic,I just use the code like this,and it works well.
List<Student> result2 = StudentRepository.GetStudent(sex,age).ToList();
IQueryable rows2 = result2.AsQueryable();
But when I change it to dynamic,it is wrong.
dynamic result = GetPeopleData(sex,age);
IQueryable rows = result.AsQueryable();
and I add a method like this,I build the project it show that List do not have the AsQueryable method.How to change it?
private dynamic GetPeopleData(int sex, int age)
{
if(sex>30)
return StudentRepository.GetStudent(sex,age).ToList();
else
return TeacherRepository.GetTeacher(sex, age).ToList();
}
AsQueryable() is an extension method and those don't work on dynamic.
Depending on what you want to do, there are several possible solutions:
Don't use dynamic. Instead, make Student and Teacher implement a common interface (say, IPerson) and use that:
private IReadOnlyList<IPerson> GetPeopleData(int sex, int age)
{
if (sex > 30)
return StudentRepository.GetStudent(sex, age).ToList();
else
return TeacherRepository.GetTeacher(sex, age).ToList();
}
…
var result = GetPeopleData(sex, age);
IQueryable<IPerson> rows = result2.AsQueryable();
Call AsQueryable() as a normal static method:
dynamic result = GetPeopleData(sex, age);
IQueryable rows = Queryable.AsQueryable(result);
BTW, checking whether sex is over 30 doesn't make any kind of sense to me. You should probably rethink that part of your design.
It is similar to this question.
As #jbtule said,Anonymous types are internal, if you cross assembly boundaries dynamic can't resolve the property.
Rather than using an anonymous type, try using an actual type or an Expando Object.
I create a simple stored procedure with some joins with the customer table and other related tables, which takes in two parameters. I can execute this SP in SQL and works.
I drag and drop this SP to my DBML file and recompile.
I add the below code in order to call the SP and return it in a List
public IQueryable<Entities.Customer> AllCustomerRanges(int CId, int ItemID)
{
List<Entities.Customer> c = myDataContext.spCustomerRanges(CId, ItemID).ToList();
}
This gives me the error:
Cannot implicitly convert type 'System.Collections.Generic.List< spCustomerRangesResult>' to 'System.Collections.Generic.List< Entities.Customer>'
Now i dont have a class spCustomerRangesResult but after some research I'm puzzled if i have done something wrong or if i need to implement a class with all the properties that the Customer class has (which sounds a little long winded) or if i've just made an error.
Any idea of how i can call a SP which shows the data in a List?
new class spCustomerRangesResult automatically generated based on sp result, you should convert it to Entities.Customer like this:
public IQueryable<Entities.Customer> AllCustomerRanges(int CId, int ItemID)
{
var c = myDataContext.spCustomerRanges(CId, ItemID).ToList();
if (c == null)
return null;
var customers = c.Select(a => new Entities.Customer
{
FirstName=a.spResultFirstName,
LastName = a.spResultLastName
//this just example conversion, change it as needed.
});
return customers;
}
please note, that I return IQueryable even though the approach that you take when using ToList() but yet returning IQuerybale may not be needed. I dont know all details so this only to show how to convert but the whole method may need re-factoring.
I'm a bit confused atm and i dont think it could be this hard as then i must be doing something wrong. What i am trying to do now for the past 2 days is to access a method inside an object that is stored in an List and i just cant get at it. In my mind it should just be to get the object back to its originated type and invoke the method but i just cant do it.
I been reading alot about Type, Generics and reflection but it cant get anyting to work so i am obviusly doing it all wrong and i need help finding the light!
Heres the latest code i have tried
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(frsttype);
String str = generic.Invoke(method);
What i am trying to reach is this method inside the object:
public override string GetCustomerSpecificData()
{
string strout = string.Format("{0,-5}{1,26}{2,28}{3,28}\a", ID, Name, Age, Gender);
string strInfo = Extra;
strout += (string.IsNullOrEmpty(strInfo) ? string.Empty : strInfo);
if (m_specialoffer)
{
strout += string.Format("\nSpecial Offer");
}
if (IsRegularCustomer)
{
strout += (IsDangerus ? "\nIs a regular customer " : "\nIs not a regular customer.");
}
strout += Environment.NewLine + PaymentInfo();
strout += (m_CarPark ? "\nHas car parked in garage." : "\nDoes not have car parked in garage.");
return strout;
}
I hope someone can point me in the correct direction as i dont think i am getting anywhere with this one :/
Any help and hints will be greatly appreciated!!! All will be upvoted for replies!
Regards
There a few things here that you need to do, firstly lets look at the codeyou posted
First question you need t ask youself is Do I need to use reflection, can I instead use interfaces or return a type that I know?
Do you have control of the GetMainList(x)? If so cant you change it so it returns something more useful other then a object?
Object customer = Hotel.Main.Manager.GetMainList(x);
Can you cast to anything?
Secondly your target method is no a generic method so the line below is not going to work.
MethodInfo generic = method.MakeGenericMethod(frsttype);
You are also invoking the method incorrectly you Invoke has two arguments the first one is the target object you wish to invoke the method against and the parameters you can pass into it.
Invoke(object obj, object[] parameters)
To invoke you method you need to the following.
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
String str = method.Invoke(customer, null) as string;
There is some great questions and community wikis on stackoverflow and of course there is many tutorials and example in the MSDN library.
A nice tutorial for reflection in .net can be found below.
Reflection in C# Tutorial
i mean you can easy invoke it :
Type myType =customer.GetType();
MethodInfo method = typeof(customer).GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(myType);
var res= generic.Invoke(this, null);
Closest to what you currently have this could work without relying on reflection.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var custObj = customer as Customer;
if (custObj !=null)
{
result = custObj.GetCustomerSpecificData();
}
var specialcustObj = customer as SpecialCustomer;
if (specialcustObj !=null)
{
result = specialcustObj.GetCustomerSpecificData();
}
/* etc */
Or, If you can change the implementation of the different types in the List have an interface (or alternative an (abstract) base class.
/* alternatively name it ISpecificData if you want adhere common used standards */
public interface SpecificData
{
string GetCustomerSpecificData();
}
and for your Customer and other classes that can be in the list :
public class Customer:SpecificData
{
/* rest of implemementastion stays the same */
}
Your code to Get a customer would go like this, and will work for every object in the list that implemented the interface.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var interfaceObj = customer as SpecificData;
if (interfaceObj != null)
{
result = interfaceObj.GetCustomerSpecificData();
}
When you know that only a specific interface will be in the list you can use the generic list to only hold object for that specific type:
mainlist = new List<SpecificData>();
and you can adapt GetMainList to only return the interface SpecificData
I have two similar methods that basically does the same thing only with different objects.
What's the best way to make a generic method out of this if possible?
The two objects:
public class StoreObject {
int Key;
string Address;
string Country;
int Latitude;
int Longitude;
}
public class ProjectObject {
int ProjectKey;
string Address;
string Description;
}
The two methods that I potentially want to make into a generic:
public StoreObject GetStoreByKey(int key)
{
using (DBEntities dbe = new DBEntities())
{
StoreObject so = new StoreObject();
var storeObject = (from s in dbe.StoreTables
where s.Key == key
select s).First();
so.Key = storeObject.key;
so.Address = storeObject.address;
so.Country = storeObject.country;
so.Latitude = storeObject.latitude;
so.Longitude = storeObject.longitude;
return so;
}
}
public ProjectObject GetProjectByKey(int projectKey)
{
using (DBEntities dbe = new DBEntities())
{
ProjectObject po = new ProjectObject();
var projectObject = (from p in dbe.ProjectTables
where p.ProjectKey == projectKey
select p).First();
po.Key = projectObject.p_key;
po.Address = projectObject.p_address;
po.Description = projectObject.p_description;
return po;
}
}
I must note that:
- I have no control over how the table fields are named (ie. p_description).
- StoreTable in the DB, for example, may have other properties (like telephone, postal code, etc) but I'm only interested in showing what I've shown in the code.
- The same goes for the ProjectTable.
Well, the tricky part is that your entities have different properties, so using generics to populate the different properties within one method will not be worth it. But you can return the whole object and then just use the properties you are interested in.
public T GetEntityByKey<T>(int key)
{
using (DBEntities dbe = new DBEntities())
{
return = dbe.StoreTables.Set<T>.Find(new object[] {key});
}
}
And to use it
StoreObject so = GetEntityByKey<StoreObject>(123);
if(so != null)
{
int lat = so.Latitude;
}
You can indeed abstract out the type returned, and factor the using, but for the rest you'd need either a switch on the type requested or, reflection to pass in the fields to retrieve as parameters and the DB query to use.
The former would be bad practice and brings little to the equation, and the latter is costly and can get messy.
This is not really a good candidate for generics, unless you have many of such look-alike methods, in which case I'd go for the reflection approach.
HTH,
Bab.
It's very unlikely that this is your entire 'unit of work' and thus the use of a fresh DBEntities() context in each of these methods is probably the root of your problem here.
Creating a Repository class that includes an instance of the DBEntities class for a single web request (or whatever other unit of request you have in your application) and which has these methods in it would be a better approach to eliminating the duplicate code here. The scope of the using() is then outside these methods and hopefully tied to your web request or other unit of time.
As an option instead of creating a new class you could also extend the DBEntities partial class to include methods like these (assuming this is generated code).
You essentially have two different functionalities in each method:
Query an entity
Map that entity to another type
The first part has been addressed by Steve Mallory.
For the second part, you can use a mapper framework to handle copying values from one instance to another. Since the names of each type do not match, you'll need to tell it how to map names (in your example, adding "p_" and making it lowercase). One possibility would be Emit Mapper.
If you were to factor out all commonality, it would be something like:
public TResult GetById<TResult, TEntity>(int id)
{
using (DBEntities dbe = new DBEntities())
{
T result = dbe.StoreTables.Set<T>.Find(new object[] {key});
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<TEntity, TResult>(
new DefaultMapConfig().MatchMembers((m1, m2) => "p_" + m1.ToLower() == m2));
return mapper.Map(result);
}
}
When I do a query that returns an anonymous type
var assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new {p.Asset, p.Asset.PropertyTbl};
Can I type the return to anything other than var?
You cannot* return an anonymous type because the caller would not know what type it is and wouldn't be able to use it.
If you want to return the results, you can create objects of a non-anonymous type:
IEnumerable<Foo> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new Foo { Bar = p.Asset, Baz = p.Asset.PropertyTbl};
You can also use the Tuple type in .NET 4 if you don't want to create a custom class for your values.
* This is not strictly true - it is possible but you should avoid doing it. Here is a link anyway if you really want to.
You can use object or dynamic (in .NET 4.0) instead of var but don't expect to find a name to an anonymous type. In your case using var is better as it will preserve the strong typing at least until you leave the scope of the current method.
You could define a new class:
public class AssetProp
{
public virtual string Asset {get;set;}
public virtual string PropertyTbl {get;set;}
}
And then you can return it as that class:
IEnumerable<AssetProp> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new AssetProp {p.Asset, p.Asset.PropertyTbl};
Not really, since the new {p.Asset, p.Asset.PropertyTbl} code creates an anonymous type. Even using object doesn't really gain you much since you can't cast it to anything useful later on, so you would have to use reflection to access the properties.
Not really. If you cast to object you wont be able to access the properties of your anonymous class.
The var keyword was specifically introduced for dealing with anonymous classes - why would you want to avoid it? If you need to return the data you should name the class.
You can if you use lambda expressions, otherwise you can do a cast but do some good exception handling.
you can also do this (it does relate much to your problem though, because you just move "var" somewhere else, but it's interesting that it recognize those types as same)
var element = new { id = 7 };
List<object> collection = new List<object>();
element = collection.Select(item => new { id = 0 }).First();