Universal data fetcher from similar entities - c#

I have many similar EF5 entities for reference data. For example:
ConsultationType entity
public class ConsultationType
{
public ConsultationType()
{
this.Appeals = new HashSet<Appeal>();
}
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Appeal> Appeals { get; set; }
}
LawBranch entity
public class LawBranch
{
public LawBranch()
{
this.Appeals = new HashSet<Appeal>();
}
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Appeal> Appeals { get; set; }
}
DbSet's in DB context
public DbSet<LawBranch> LawBranches { get; set; }
public DbSet<ConsultationType> ConsultationTypes { get; set; }
As you see these entities have similar properties Id and Title.
The actual problem
I need a function that fetches data from database and puts it into list. Then the function inserts predefined object as a first element of this list.
My predefined object:
private class PredefinedReferenceItem
{
public int Id { get; set; }
public string Title { get; set; }
public PredefinedReferenceItem()
{
this.Id = -1;
this.Title = "some text";
}
}
My wrong solution:
Firstly, I created interface IReference that describes reference entity
public interface IReference
{
int Id { get; set; }
string Title { get; set; }
}
Secondly, my reference entities realize this interface
public class ConsultationType : IReference { ... }
public class LawBranch: IReference { ... }
Thirdly, I created the function
public IList<IReference> GetReferenceWithPredefinedItem<T>(DbSet<IReference> dbset)
{
var data = from a in dbset
orderby a.Title
select a;
var list = data.ToList();
list.Insert(0, new PredefinedReferenceItem());
return list;
}
But the function doesn't work in my viewmodel:
return GetReferenceWithPredefinedItem(dbContext.ConsultationTypes);
Error message in VS2012:
The type arguments for method 'Mjc.Client.ViewModels.ViewModelBase.GetReferenceWithPredefinedItem<T>(System.Data.Entity.DbSet<Mjc.Foundation.IReference>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Please help me to find an error or specify the right direction.

You new to change the GetReferenceWithPredefinedItem to use generics with IReference constraint on generic type T, the method should look like:
public IList<T> GetReferenceWithPredefinedItem<T>(DbSet<T> dbset) where T:IReference
{
var data = from a in dbset
orderby a.Title
select a;
var list = data.ToList();
list.Insert(0, new PredefinedReferenceItem());
return list;
}

alexandr-mihalciuc provided right solution. My addition to resolve type reference error:
Final solution:
public List<TEntity> GetReferenceWithPredefinedItem<TEntity>(DbSet<TEntity> dbset) where TEntity : class, new(), IReference
{
var data = from a in dbset
orderby a.Title
select a;
var list = data.ToList();
list.Insert(0, new TEntity() { Id = -1, Title ="aefaef" });
return list;
}

Related

How can I write generic query in asp .net mvc for 3 different tables in db context with LINQ?

I have 3 different tables and 3 different columns which are primary columns.
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
}
public class Teacher
{
public int TeacherId { get; set; }
public string Name { get; set; }
}
public class Lesson
{
public int LessonId { get; set; }
public string Name { get; set; }
}
var _context = new MyContext()
_context.Student(x=>x.StudentId == someValue)
or
_context.Teacher(x=>x.TeacherId == someValue)
or
_context.Lesson(x=>x.TeacherId == someValue)
only changing table name and key value.
What is the best way using generic method?
Make a general repository and than inherit it with native repository of each class like this
public class GeneralRepository<T> where T : class
{
private MyContext Context = new MyContext();
protected DbSet<T> Dbset { get; set; }
public GeneralRepository()
{
Dbset = Context.Set<T>();
}
public T SelectByID(int? id)
{
var Record = Dbset.Find(id);
return (Record);
}
public class StudentRepositoy :GeneralRepository<Student>
{
}
Make a object of native repositry and call the function
Where ever you need to call the function make a object of student repository
StudentRepositoy repository = new StudentRepositoy();
var result = repository.SelectByID(3);
You can consider to use a pattern like "generic repository". Or you can simply write a method takes Expression> as input and pass the expression to it.
https://code.msdn.microsoft.com/generic-repository-pattern-ddea2262
https://www.codeproject.com/Articles/814768/CRUD-Operations-Using-the-Generic-Repository-Patte

EF - not supported in LINQ to Entities

I am trying to list some food items with a controller. I use Repository pattern with UnitOfWork for the data in another assembly and referenced it in a BaseApiController. The Data property is my UnitOfWork instance.
var result = Data.Food
.FindAll()
.Select(FoodItemViewModel.Create);
return result;
and here is my ViewModel:
public static Expression<Func<FoodItem, FoodItemViewModel>> Create
{
get
{
return fi => new FoodItemViewModel
{
Id = fi.Id,
Description = fi.Description,
DiaryEntries = fi.DiaryEntries
.Select(s => new DiaryEntityViewModel()
{
Id = s.Id,
Quantity = s.Quantity
}
};
}
}
But all I get is:
"The specified type member 'DiaryEntries' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported."
My DiaryEntries member in the ViewModel is
IEnumerable<DiaryEntityViewModel>
and my DiaryEntries member in the Data instance is
IRepository<DiaryEntry>
and DiaryEntry is my model class
and here is my FoodItem model class:
public class FoodItem
{
private IEnumerable<Measure> measures;
private IEnumerable<DiaryEntry> diaryEntries;
public FoodItem()
{
this.measures = new HashSet<Measure>();
this.diaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual IEnumerable<DiaryEntry> DiaryEntries
{
get
{
return this.diaryEntries;
}
set
{
this.diaryEntries = value;
}
}
public virtual IEnumerable<Measure> Measures
{
get
{
return this.measures;
}
set
{
this.measures = value;
}
}
}
Change you FoodItem class to the one below, IEnumerable<T> is not supported as a type for a navigation collection :
public class FoodItem
{
public FoodItem()
{
this.Measures = new HashSet<Measure>();
this.DiaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<DiaryEntry> DiaryEntries
{
get;
set;
}
public virtual ICollection<Measure> Measures
{
get;
set;
}
}

LinqToEntities Generic Classes and Interface

public class CoreEntity
{
public int Id { get; set; }
//some additional common fields like insertedBy, insertedAt etc
}
public interface IUniqueObject
{
Guid Uid { get; set; }
}
public class Tag : CoreEntity
{
public string TagName { get; set; }
}
public class ItemTags : CoreEntity
{
public Guid TaggedItemUid { get; set; }
public int TagId { get; set; }
}
public class Repository<T>
where T : CoreEntity
{
public DbSet<T> TheEntitySet { get; set; }
public DbSet<ItemTags> ItemTagSet { get; set; }
public IQueryable<T> AddTagsFilter(IQueryable<T> query, List<int> tags)
{
var ts = ItemTagSet;
if (typeof (T) is IUniqueObject)
{
query = query.Where(f => ts.Any(e => e.TaggedItemUid == ((IUniqueObject)f).Uid && tags.Contains(e.TagId));
}
return query;
}
}
//usage
public class Departman : CoreEntity, IUniqueObject
{
public Guid Uid { get; set; }
//some other fields
}
class Test
{
void xx()
{
var r = new Repository<Departman>();
}
}
The classes are simplified for the purpose of the problem they do much more than shown here. The thing is that Linq to Entities does not allow casting ((IUniqueObject)f).Uid. So how can apply the Tags Filter knowing that generic type T implements IUniqueObject interface. I tried using some GenericMethod with "T2 where : CoreEntity , IUniqueObject" but I could not cast type T to T2, anyways
any help is appreciated.
The Where statement in LinqToEntities will be converted to SQL syntax. Every instruction must be translatable into SQL - which means no casting. Still thinking about a more helpful answer. I think you'll need to have distinct functions for the different query types, e.g. add a method like:
public IQueryable<T> AddTagsFilterToUniqueObject(IQueryable<T> query,
List<int> tags) where T : IUniqueObject
{
var ts = ItemTagSet;
query = query.Where(f => ts.Any(e => e.TaggedItemUid == (f.Uid && tags.Contains(e.TagId));
return query;
}

C# cast to another class with recursion

I am developing a MVC Project with Entity framework and i have a category table like this :
public partial class Categories
{
public Categories()
{
this.Categories1 = new HashSet<Categories>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public Nullable<int> RelatedCategoryId { get; set; }
public virtual ICollection<Categories> Categories1 { get; set; } //Children
public virtual Categories Categories2 { get; set; } //Parent
}
When i get table data with EF, it gives me the object i want. Parents with children.
class Program
{
static Entities db = new Entities();
static void Main(string[] args)
{
List<Categories> categories = db.Categories.Where(item => item.RelatedId == null).ToList();
}
}
With relatedId == null part, i get the main categories which has no parent.
There is no problem this far. But i want to cast categories object which ef returned to another class which is :
public class NewCategories
{
public int Id { get; set; }
public string Name { get; set; }
private List<NewCategories> _subCategories;
public NewCategories()
{
_subCategories= new List<NewCategories>();
}
public List<NewCategories> SubCategories { get { return _subCategories; } }
}
And i want new List<NewCategories> newCategories object.
How can i accomplish that?
Thanks.
I think you have to create a recursive method to convert Categories to NewCategories, something like this (I'm not sure if it works, but it's worth trying):
public NewCategories ConvertToNewCategories(Categories cat){
NewCategories nc = new NewCategories {Id = cat.CategoryId, Name = cat.CategoryName};
nc.SubCategories.AddRange(cat.Categories1.Select(c=>ConvertToNewCategories(c)));
return nc;
}
//Then
List<NewCategories> categories = db.Categories.Where(item => item.RelatedId == null)
.Select(item=>ConvertToNewCategories(item))
.ToList();

Linq query results to List collection

I use the code below to get records from two tables but I want to have the result in a List. I used the code below, but this error occured:
Cannot implicitly convert type
System.Collections.Generic.List<AnonymousType#1> to
System.Collections.Generic.List<QuickRoutes.DAL.Route>
public List<Route> GetAllForUser(Guid userId)
{
// var result = new List<Route>();
aspnetdbDataContext aspdb = new aspnetdbDataContext();
List<Route> result = (from r in aspdb.Routes
where r.UserId == userId
join t in aspdb.TrackPoints on r.RouteId equals t.RouteFK
select t.TrackPointId,t.RouteFK,t.TrackTime,t.Longitude,t.Latitude,t.Elevation, r.SourceName).ToList();
return result;
}
EDIT:
I want to match my query with this class:
namespace QuickRoutes.Entities
{
public enum RouteType
{
Route, Waypoints, Track
}
public sealed class Route
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Time { get; set; }
public List<TrackPoint> TrackPoints { get; set; }
public Guid UserId { get; set; }
public string GpxData { get; set; }
public RouteType RouteType { get; set; }
public Route(int id)
{
Id = id;
TrackPoints = new List<TrackPoint>();
}
......
but if I change my code to this one,It doesn't work too
List<Route> result = (from r in aspdb.RouteLinqs
where r.UserId == userId
join t in aspdb.TrackPointlinqs on r.RouteId equals t.RouteFK
select r).ToList();
to be honest I want to change this function that is written with SQL to LINQ,have any better idee?
namespace QuickRoutes.Entities
{
public enum RouteType
{
Route, Waypoints, Track
}
public sealed class Route
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Time { get; set; }
public List<TrackPoint> TrackPoints { get; set; }
public Guid UserId { get; set; }
public string GpxData { get; set; }
public RouteType RouteType { get; set; }
public Route(int id)
{
Id = id;
TrackPoints = new List<TrackPoint>();
}
......
It's because your projection (what you're selecting) isn't the same as a Route (what you're trying to return);
If you wish to return a Route Change your select to:
select r
If you wish to return your joined projection, you'l have to create a new type, and return that:
e.g:
public class RouteTrack
{
public TrackPointId ...
public RouteFK ...
public TrackTime ...
}
And return a List<RouteTrack> from your method, and change your select to:
select new RouteTrack { TrackPointId = t.TrackPointId .... }
Since your query selects properties from two different tables, it obviously no longer returns objects of type Route, but a new type of object (an anonymous type).
If you want to return this new type, you need to define it explicitly, then return the data using this type.
public class MyClass
{
public MyClass(int trackPointId, DateTime trackTime, ...)
{
TrackPointId = trackPointId;
TrackTime = trackTime;
...
}
public int TrackPointId { get; set; }
public DateTime TrackTime { get; set }
...
}
List<MyClass> result = (from r in aspdb.Routes
where r.UserId == userId
join t in aspdb.TrackPoints on r.RouteId equals t.RouteFK
select new MyClass(t.TrackPointId,t.RouteFK,t.TrackTime,t.Longitude,t.Latitude,t.Elevation, r.SourceName);
I have faced the same. The issue here is that you are creating an anonymous type that contains fields from both the Route and aspdb.TrackPoints.
Hence you cannot cast this to List.
This is a mapping problem.
To resolve this you need to create a newcustom class that contains all the fields/properties that you are getting out from the query.
Let's say you create a custom class say RouteMapper then map using code like
select new RouterMapper
{
RouteMapper.TrackPointId = t.TrackPointId, RouteMapper.RouteFK=t.RouteFK, RouteMapper.SourceName = r.SourceName
}
This way now you return List< RouteMapper>.

Categories