EF4 and MVC3 mapping ViewModels to Entities - c#

I have an EF4 Entity Workgroup. Below is the meta-data for that model for reference.
[MetadataType(typeof(WorkgroupMetaData))]
public partial class Workgroup {
public Contact manager { get; set; }
}
[Bind(Exclude = "id")]
public class WorkgroupMetaData
{
[ScaffoldColumn(false)]
public int id { get; set; }
[DisplayName("Org. Number")]
[Required(ErrorMessage = "Org. Number is required.")]
public string org_number { get; set; }
[DisplayName("Workgroup Name")]
[Required(ErrorMessage = "Workgroup name is required.")]
public string name { get; set; }
[DisplayName("Customer Contact")]
public int customer_contact_id { get; set; }
[DisplayName("Manager")]
public int manager_id { get; set; }
[DisplayName("Tech. Lead")]
public int lead_id { get; set; }
[DisplayName("Time Approver")]
public int time_approver { get; set; }
[DisplayName("Description")]
public string description { get; set; }
[ScaffoldColumn(false)]
public object created_at { get; set; }
[ScaffoldColumn(false)]
public object last_modified_at { get; set; }
}
I've got a ViewModel defined as:
public class WorkgroupViewModel
{
public Workgroup Workgroup { get; set; }
public List<Workgroup> Workgroups { get; set; }
}
On the view I have a grid to dump out the workgroups available. This works but I was wondering how to convert the ID fields to the actual strings from another table. Basically the manager, customer_contact, lead are all references to the Contact entity. I would like to show the names from Contacts instead of just the id.
How can this be accomplished? I've looked around a bit but I can't seem to find a suggestion or an answer. Maybe I looking at this from the wrong perspective?

You might consider using a wrapper around Workgroup (decorator pattern) or Tuple or creating a custom class that binds them together.
public class WorkgroupDisplayModel
{
public Workgroup Workgroup { get; set; }
public Manager Manager { get; set; }
// Add additional properties for each related type
}
In your EF query you can do something like:
var query = from w in Context.Workgroups
join m in Context.Managers
on w.manager_id equals m.uid
// Additional joins for each related table
where w.Description == "Project 1" // Whatever criteria
select Tuple.Create(w, m); // Add param for each type
//or
//select new WorkgroupDisplayModel { Workgroup = w, Manager = m, ... };
var list = query.ToList();
var contact = list[0].Item1; // Tuple has strongly typed Item1 thru ItemN
var manager = list[0].Item2;
Then your view model could have:
List<Tuple<Workgroup, Manager, Customer, Lead>> Workgroups { get; set; }
or
List<WorkgroupDisplayModel> Workgroups { get; set; }

Related

EF Core - Create report table from multiple tables

I have a report that I need to send to my react frontend that needs to be easily queried and searched. The problem is with the current method we need to pull all the entire database before performing a query due to nested objects and other factors.
To significantly speed up the process I want to create a Report Table/View to query from that stays up to date as the other tables change.
Here is a small example of the models:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int TypeId { get; set; }
public ItemType Type { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Order
{
public int Id { get; set; }
public Customer Customer { get; set; }
public List<Item> Items { get; set; }
}
public class ItemReport
{
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; }
public string Customer { get; set; }
public ItemReport(Item item)
{
Name = item.Name;
Description = item.Description;
Type = item.Type.Name;
Customer = item.Order.Customer.Name;
}
}
ItemReport is the model I use to send to the frontend.
I've read a fair amount on Keyless Entities and Views, but am in need of a little guidance on putting all the pieces together.
Currently we would pull all Items and required fields like Name from customer and turn it into an IEnumerable list of ItemReport to then be filtered/sorted or searched.
As a side note, there may be other solutions than the on I'm posting for that I would be open to as well.
I've looked into this quite a bit, but I don't think I'm finding the right solutions as an example when reading on Views they mention how they cannot be inserted into or updated with EF Core.
It is simple projection. Passing item in Constructor is not right way, because EF Core cannot look into compiled method body.
var query = context.Items
.Select(item => new ItemReport
{
Name = item.Name;
Description = item.Description;
Type = item.Type.Name;
Customer = item.Order.Customer.Name;
});

Entity Framework Core filter related entities and get top 2 for each group

I am using Entity Framework Core 2.0.1 and I have the following models
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Slug { get; set; }
public int Approved { get; set; }
public DateTime ArticleDate { get; set; }
// ... some other fields
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategory
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
//... soem other fields
[ForeignKey("ArticleCategoryParent")]
public int? ArticleCategoryParentID { get; set; }
public virtual ArticleCategory ArticleCategoryParent { get; set; }
public virtual ICollection<ArticleCategory> SubCategories { get; set; }
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategoryRelation
{
[Column(Order = 0)]
public int ArticleId { get; set; }
public Article Article { get; set; }
[Column(Order = 1)]
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory {get; set;}
}
Every article belongs to one or more categories. Categories might have parent category.
I want to get from database last two articles (where Approved = 1) with related category details, for each category that belongs to a parent category which id is given as input.
I have tried but with no success. I can't filter results of an .Include() entity. Is it possible... or I don't know how to do it?
All my data are accessed through entity framework with appContext (the context used to get entities from database). Can I achieve what I want through entity framework core (lambda expression is preferred over Linq if possible), or should I use ADO.NET library (which I know how to execute custom queries).
P.S. I want to get data only to show in the view... no edit is needed.
You don't actually need to include here at all, as far as I can tell. Whenever you use data from a nav property, EF will go get the data from that table, as best it can filter it.
var CategoriesUnderParent = AppContext.ArticleCategories
.Where(c => c.ArticleCategoryParent == {parent});
foreach(var category in CategoriesUnderParent)
{
var ArticlesAllowed = category.ArticleCategoryRelations
.Where(acr => acr.Article.Approved == 1).Select(a => a.Article);
var ArticlesPicked = ArticlesAllowed
.OrderByDescending(ar => ar.ArticleDate)
.Take(2);
// Do something with your data
}

2 tables in Local DB in MVC using a foreign key to interact - Error, returns "The property cannot be configured as a navigation property

So my teammates and I are building a website that aggregates textbook prices from different textbook websites (we aren't dynamically getting the prices from the websites themselves, but for the purposes of the school project we are in, we are just randomly entering them in).
So I've got two tables in my database Book1 - a "Books" table and a "Prices" table. I have a foreign key in my Prices table (screenshot of design view) that relates back to my Books table (screenshot of design view).
The ISBN field of our Prices table is a foreign key to the ISBN field of our Books table.
We believe we've set everything else up correctly but when we run it, it returns an exception saying:
The property 'ISBN' cannot be configured as a navigation property. The
property must be a valid entity type and the property should have a
non-abstract getter and setter. For collection properties the type
must implement ICollection where T is a valid entity type.
Here are all the relevant classes.
Book.cs
public partial class Book
{
[Key, Required]
[StringLength(14)]
public string ISBN { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public string YearPublished { get; set; }
public virtual Price Price { get; set; }
}
Price.cs
public partial class Price
{
[Key, Required]
public int PriceID { get; set; }
[ForeignKey("ISBN")]
public string ISBN { get; set; }
public decimal? AmazonPrice { get; set; }
public decimal? BarnesAndNoblePrice { get; set; }
public decimal? CheggPrice { get; set; }
public decimal? SecondAndCharlesPrice { get; set; }
public decimal? AlibrisPrice { get; set; }
public decimal? ThriftBooksPrice { get; set; }
public decimal? ValoreBooksPrice { get; set; }
public virtual Book Book { get; set; }
public virtual IEnumerable<Book> Books { get; set; }
}
MyModel.cs
public class MyModel : DbContext
{
public MyModel()
: base("name=DefaultConnection") { }
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<Price> Prices { get; set; }
}
HomeController.cs
public ActionResult Index()
{
var model = new IndexVM();
var ctx = new MyModel();
foreach (var bk in ctx.Books)
{
model.Books.Add(bk);
}
return View(model);
}
We've been told by our professor that it has something to do with the IEnumerable class, but we've tried every combination in the book and we still can't get it to work.
Any and all help will be appreciated.
In Price.cs
[ForeignKey("ISBN")]
public string ISBN { get; set; }
should read
[ForeignKey("Book")]
public string ISBN { get; set; }
Additionally in Book.cs I think you need somewhere to store the cross reference to Price
[ForeignKey("Price")]
public int PriceId { get; set; }
this question describes the same issue Foreign Key Annotation in MVC

Entity Framework: map multiple similar classes to one table in similar databases

I have model with table in databases of my clients:
public class Doctor
{
public int Id { get; set; }
public int Filial { get; set; }
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public int? DepartmentId { get; set; }
public Department Department { get; set; }
}
public class DoctorConfiguration : EntityTypeConfiguration<Doctor>
{
public DoctorConfiguration()
{
ToTable("DOCTOR");
Property(d => d.Id).HasColumnName("DCODE").IsRequired();
Property(d => d.Filial).HasColumnName("FILIAL");
Property(d => d.ShortName).HasColumnName("DNAME");
Property(d => d.FullName).HasColumnName("FULLNAME");
Property(d => d.Phone).HasColumnName("DPHONE");
Property(d => d.DepartmentId).HasColumnName("DEPNUM");
HasKey(d => d.Id);
HasOptional(d => d.Department).WithMany(dep => dep.Doctors).HasForeignKey(d => d.DepartmentId);
}
}
Recently additional clients came. Their databases has mostly the same models, but some fields had changed types from int to long.
The new Doctor model looks like:
public class Doctor
{
public long Id { get; set; }
public long Filial { get; set; }
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public long? DepartmentId { get; set; }
public Department Department { get; set; }
}
How to properly map new Doctor model format to the same table "DOCTOR"?
The application uses Firebird database. Version for "old" clients doesn't support long numbers format.
In case of creating similar Doctor configuration, an error appears:
"The entity types 'DoctorInt' and 'Doctor' cannot share table 'DOCTOR' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them."
I know about Table-per-Hierarchy (TPH) Inheritance. Looks like it can't help in this situation.
The type Doctor is only the one of the many similar types with this problem. The code in an application is connected with first models format. I wouldn't like to change it all...
I would like to reuse existing functionality.
If I don't misunderstand, you need to support old and new databases with the same code and databases only differs on the size of IDs
An approach is to use generics and conditional compilation
public class Doctor<T> {
public T Id { get; set; }
public int Filial { get; set; } //Suposing Filial is not a foreing key
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public T? DepartmentId { get; set; }
public Department Department { get; set; }
}
When istantiating:
#ifdef USELONG
var d = new Doctor<long>();
#else
var d = new Doctor<int>();
#endif
Or with a factory pattern (where CreateDoctor may be a static method of Doctor class):
var d = Doctor.CreateDoctor();

Entity Framework 6 way to check at runtime if navigation property is many-to-many or one-to-many

For example, I have the following classes
class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Team> Teams { get; set; }
public ICollection<Address> Addresses { get; set; }
}
class Team
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
class Address
{
public int Id { get; set; }
public string AddressType { get; set; }
public string Address { get; set; }
public int UserId { get; set; }
}
I have a way to determine at runtime if property is navigation property using ObjectContext metadata thanks to
entity framework check if property is navigation property
But what I need additionally is to know if property is many-to-many (like Teams property in the example above) or one-to-many (like Addresses). Is there a way to do this?
In case someone will need same thing - here is some way of doing it:
var navigationProperties = objectContext.MetadataWorkspace
.GetItems<EntityType>(DataSpace.OSpace)
.Single(p => p.FullName == typeof(User).FullName)
.NavigationProperties;
var one = navigationProperties.Where(navigationProperty => navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One).ToList();
var many = navigationProperties.Where(navigationProperty => navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many).ToList();

Categories

Resources