Passing generic class name in dbcontext - c#

My entities context file code:
public partial class MyDbContext : DbContext
{
//dbset 1
public DbSet<Customer> Customers { get; set; }
//dbset 2
public DbSet<Order> Orders { get; set; }
}
Class Shipments has a method GetCustomerName
public class Shipments
{
public string GetName(object caller, System.Data.Entity.DbSet objectContext)
{
//This one passes.
IQueryable<Customer> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
//This one fails
IQueryable<caller.GetType()> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
}
}
Question: I would like to remove the hard coding of class Customer there, instead call this method by passing the class name as parameter?
How can I implement this? In this case caller.GetType().Name didn't work for me.
GetName(Order, mydbContext);
GetName(Customer, mydbContext);
Both should work with the same code (trying it to make generic, I am not sure how to cast it generic). Any help will be great. Thanks.

Like so many people, including myself early on, you are not seeing the difference between a data type and an instance of the Type class. A data type is something that is known at compile time. When you call GetType, what you get back is an object whose data type is Type. It contains information about a data type but it is not a data type itself.
What you need to do is make your GetName method generic as well:
public string GetName<T>(T caller, System.Data.Entity.DbSet objectContext)
{
IQueryable<T> myEntity = from p in objectContext
select p where p.Id=1; //get customer name
}
To be able to do that though, the compiler must know that type T actually has an Id property. That means that T has to be constrained to be a specific base class or interface that declares an Id property. In the case of auto-generated EF classes, an interface is your only option.
There are still other issues with your code, but that covers what you are actually asking about.

I don't see the point of passing an object argument just for the sake of getting its type. You can use generics for that:
public string GetName<T>(DbSet objectContext) where T : BaseEntity {
IQueryable<T> myCustomer = from p in objectContext
select p where p.Id = 1;
//Get the name
//Return the name
}
Please note that I added a constraint for T to extend from a BaseEntity.
All your entities should then extend from this abstract class (which should contain the property Id and Name for getting the name).
You would then be able to retrieve the name of, not only Customer entities, but even Order entities:
string customerName = GetName<Customer>(context);
string orderName = GetName<Order>(context);
If only your Customer entity has the property Name, then don't use generics at all, you would be better defining the type explicitly:
public string GetName(context) {
IQueryable<Customer> customer = from p in context
select p where p.Id = 1;
//Get the name
//Return the name
}

Related

Expression<TDelegate> on EF Include Statement

Hello so I am trying to make an some a bit more dynamic, that said i would like to be able to pass in an expression that will include the entities that I am trying to include. when i am trying to do this i keep getting an error that says:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I have googled this error and saw that they were doing what i can get to work:
context.Contacts.Include(contact=>contact.PhoneNumber)
what I am trying to do is this:
Func<IEntity, IEntity> func = (contact) => ((Contact)contact).PhoneNumber;
Expression<Func<IEntity, IEntity>> expression = (contact)=> func(contact);
context.Contacts.Include(expression);
can someone please expain what I am doing wrong and why?
public interface IEntity
{
int Id { get; set; }
}
public class Contact:IEntity
{
public int Id {get;set;}
public string FirstName { get; set; }
public string LastName { get; set; }
public int PhoneNumberId { get; set; }
public PhoneNumber PhoneNumber { get; set; }
}
public class PhoneNumber:IEntity
{
public int Id { get; set; }
public string Number { get; set; }
}
UPDATE:
I have a repository class that looks at the type of class and uses reflection to get the correct DbSet and returns a IQueryable.
public IQueryable Get(IEntity t)
{
var setMethod = typeof(DbContext)
.GetMethod(nameof(DbContext.Set))
.MakeGenericMethod(t.getType());
var query = (IQueryable)setMethod.Invoke(db, null);
var results = query...
}
I have a table control that goes and gets the correct data using the typeRepository. So I am trying to be able to include entities to that table.
When you don't have a generic type argument, but simple Type parameter, you'd better use the non generic DbContext and IQueryable services provided by EF.
First, you don't need reflection - DbContext provides non generic Set method with Type argument:
public virtual DbSet Set(Type entityType)
As for Include, you can simply use the non generic Include extension method with string argument:
public static IQueryable Include(this IQueryable source, string path)
So the method in question can be implemented like this:
IQueryable query = db.Set(t.GetType());
if (t is Contact)
query = query.Include(nameof(Contact.PhoneNumber));
Not the best OOP practices, but works for the chosen design.
I believe your issue relates to the delegate type you are assigning to your Func. IE: Func<IEntity, IEntity> func
In which case IEntity does not have a navigation property defined of type IEntity
Try assigning the concrete type for your implementation
Func<Contact, PhoneNumber> func

Generic classes and its child

I have base class Entity and an enheritance class say Home ,
public class Home : Entity
{
public int CityId{get;set;}
}
public class Town : Entity
{
public int CityId {get;set}
public Home CityHall {get;set;}
public List<Home > Homes{get;set;}
}
I want to set the CityId for Town and its children
so a first try I did the following
public class DataAccessBase<T> where T : Entity
{
public int Add(T entity)
{
Type t = typeof(T);
PropertyInfo prop = t.GetProperty("CityId");
if (prop != null)
{
prop.SetValue(entity, 2);
}
}
}
this work only for the parent how to access children , I want to d othat generically simply because I have a dataaaccesslayer that insert of Database genrically
It looks like there are two unrelated problems
how to set property of an object without knowledge if property is there: reflection as you have it solve that. Note that this is not very C# way - you'd use some interface and restrict generics to that interface to allow strongly typed access to properties.
how to enumerate "child" objects without knowing type: traditional solution again is to add interface for "GetChildren" functionality. Alternatively you can go with reflection and find all properties that are of "child" type and combine with all properties that are of type IEnumerable<"child type">.
If you can use some convention dynamic could be easier alternative to reflection (i.e. every type exposes Children property to enumerate them:
dynamic town = GetTown();
foreach(dynamic child in town.Children) {...}
You can set the properties directly no need for reflection.
entity.CityId = 1;
if(entity is Town) {
var town = entity as Town;
if(town.Homes!=null) {
town.Homes.ForEach(t=> t.CityId = entity.CityId);
}
}

Creating common class objects based on class name and use it

I have the same code logic used across different class objects.
For ex:
var matchingTypes = from matchType in order.Contacts
select matchType;
var matchingTypes = from matchType in customer.Contacts
select matchType;
Instead of writing duplicate lines of code, i would like to pass order, customer class names and get Contacts through it, so that the above code will look like (we are using LINQ in our code)
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
The stuff i tried is passed an object parameter
GetData(object objectElement) // method consuming an object parameter.
var objectOrder= objectElement as Orders;
var objectCustomer= objectElement as Customers;
if(objectOrder!=null)
{
objectElement = (Orders) objectOrder; //type
}
if(objectCustomer !=null)
{
objectElement = (Customers) objectCustomer;
}
By doing so, i am repeating my code, which i would like to avoid, any suggestions/ideas? thanks.
I would like to use objectElement and assign only once, so that i can call like this as shown below
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
An interface would be the preferred way to do this, but you could also use dynamic to duck type a method:
public IEnumerable<Contact> GetContacts(dynamic yourObject)
{
return yourObject.Contacts;
}
Note this will not give you a compile error if you call it with something that doesn't have a property called Contacts of type IEnumerable<Contact> but will instead give you a runtime error.
Or you don't even actually need a method, you could just do this:
var matchedTypes = ((dynamic)yourObject).Contacts as IEnumerable<Contact>;
Interfaces would be a safer bet, but are a little tricky with generate entity framework classes. But you can do them becuase they are generated as partial classes. So you can do something like this:
public interface IHaveContacts
{
public IEnumerable<Contact> Contacts { get; }
}
and then:
public partial class Orders : IHaveContacts
{
// should need to do anything since the auto-genrated Contacts property
// will satisfy the interface
}
public partial class Customers : IHaveContacts
{
// ditto
}
And now you can do:
var matchedTypes = ((IHaveContacts)yourObject).Contacts;
Or, if you really, really must (which you don't):
var matchedTypes = from matchType in ((IHaveContacts)yourObject).Contacts
select matchType;
Create an interface IContactsContainer:
public interface IContactsContainer
{
public YourContactType Contacts{get;set;}
}
Then your customer and order classes can implement it:
public class Customers : IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
public class Orders: IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
After that in your method you can use:
IContactsContainer objectElement = yourOrderObject;

C# Table Per Hierarchy Projection Union

I'm using table per hierarchy to project to model classes. When I use of type in Linq to Entities, all of my results are always the last type which was projected. What I mean is if I have these for domain models:
public interface IPerson { string Name {get;set;} }
public abstract class Person : IPerson { public string Name { get;set; } }
public class Employee : Person{ }
public class Manager : Person { }
And I have a Person table with a Name and type column, and use table per hierarchy to yield me specific Entities (which I'll refer to as EmployeeEntity and ManagerEntity).
When I use this code:
IQueryable<IPerson> people = entities.OfType<ManagerEntity>().Select(x => new Manager
{ Name = x.name }).Concat(entities.OfType<EmployeeEntity>().Select(y => new Employee
{ Name = y.name }));
var projectedPeople = people.ToList();
All of the types of objects in projectedPeople are Employee, even the the objects that should be of type Manager. If I remove the Concat call and run them individually, I get the correct type back (Manager or Employee).
What am I doing wrong here? I wrote some test code that removed Linq from the equation by making dummy lists of objects and calling AsQueryable on them, followed by a projection, and that worked as it should.

EF custom selector expression

I have the following simple scenario. I have a Customer class, which is defined in my edmx. I have an Order class as well, and a Customer can have zero or more Orders through its Orders property.
I defined an OrderCount property in my Customer class like this:
public partial class Customer
{
public int OrderCount { get; set; }
}
I would like to easily write Select queries, which load the value of this OrderCount appropriately. I tried doing this in the Customer class:
public partial class Customer
{
public int OrderCount { get; set; }
public static Expression<Func<Customer, Customer>> DetailSelector = c =>
{
c.OrderCount = c.Orders.Count;
return c;
};
}
And then the Select query:
var customersWithOrderCount = ctx.Customers.Select(Customer.DetailSelector);
However, this way I get an error, that the lambda with a statement body can not be converted to an Expression.
Then I tried to do this:
public partial class Customer
{
public int OrderCount { get; set; }
public static Expression<Func<Customer, Customer>> DetailSelector = c => Customer.LoadDetail(c);
public static Customer LoadDetail(Customer customer)
{
customer.OrderCount = customer.Orders.Count;
return customer;
}
}
But this way when I execute the Select query I get the following exception:
LINQ to Entities does not recognize the method 'Customer LoadDetail(Customer)' method, and this method cannot be translated into a store expression.
What would be the easiest way to do this?
UPDATE: Just for clarification, I am specifically looking for a way to do this with an Expression. So I do not want to load the Orders from the database, I just want to fill the OrderCount property.
Thanks,
This is a bit simplistic, but I think stays with a basic working design on the entities. The best way IMHO is to use .Order.Count. If you are pulling it from there, why not keep it there?
If you want to write select queries, include the value from Customers.Orders directly and the proper SQL will be generated for you. Otherwise I would personally include this value to be set in a CustomerRepository class right after I load the customer. Then you always get your customer from one place and you know its set (implies usage of the Repository Pattern of course)

Categories