Odata v4 ODataModelBuilder relations builder - linq

i'm working to implement web api using OData v4
my database structure is seperate tables the relations between my tables should be represent inside Enitiy Framework
i had implement my EF model for my database structuer as following:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<tabl1>("tabl1").EntityType.HasKey(p => p.ID);
builder.EntitySet<tabl2>("tabl2").EntityType.HasKey(p => p.ID);
builder.EntitySet<tabl3>("tabl3").EntityType.HasKey(p => p.ID);
builder.EntitySet<tabl4>("tabl4").EntityType.HasKey(p => p.ID);
config.MapODataServiceRoute(
routeName: "ODataroute",
routePrefix: "api",
model: builder.GetEdmModel());
how can i implement relationship between my tables
so i can be able to use query like this or if i can use normal linq Query action from my controller but it's not working with me
http://localhost:13193/api/table1?$expand=table2

Don't know which EF technic you are using. If you are using code-first you just need to set the relation inside you model definition. The following model builds a 1 : n relation between tabl1 and tabl2:
public class tabl1
{
public int Id { get; set; }
public virtual tabl2 tabl2 { get; set; }
}
public class tabl2
{
public int Id { get; set; }
public virtual ICollection<tabl2> tabl2 { get; set; }
}

Related

MVC4 Entity Framework many-to-many custom join table name and schema name

I'm using database-first approach with Oracle database. In database I have 3 tables: USERS, ROLES, and join table between them: USERS_ROLES_ASSOCIATION. My model consists of two classes:
[Table("USERS", Schema = "SIGNUM")]
public class User
{
public User()
{
Roles = new HashSet<Role>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Column("USR_ID")]
public decimal UserId { get; set; }
[Column("USR_LOGIN")]
public string Login { get; set; }
[Column("USR_PASS")]
public string Password { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
[Table("ROLES", Schema = "SIGNUM")]
public class Role
{
public Role()
{
Users = new HashSet<User>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Column("ROL_ID")]
public decimal RoleId { get; set; }
[Column("ROL_NAME")]
public string RoleName { get; set; }
public virtual ICollection<User> Users { get; set; }
}
When I try to resolve user's roles:
using (var dbContext = new AppDbContext())
{
User user = dbContext.Users.FirstOrDefault(u => string.Equals(u.Login, "someusername"));
var roles = user.Roles.Select(r => r.RoleName); // exception
}
... I get the following error: "ORA-00942: table or view does not exist".
The problem is that EF is looking for table "dbo"."RoleUsers" instead of "SIGNUM"."USERS_ROLES_ASSOCIATION".
How can I specify join table name for many-to-many relationship and schema name in database-first approach?
How can I specify join table name for many-to-many relationship and
schema name in database-first approach?
You'd have to specify it manually from the fluent config code...
modelBuilder.Entity<User>()
.HasMany(x => x.Roles)
.WithMany(x => x.Users)
.Map(x => x.ToTable("UserRole", "SIGNUM"));

Mapping from ViewModel to DTo to Domain using automapper

I'm having a trouble do the following mapping:
Domain (simplified version):
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual ModelDto Model { get; set; }
}
ViewModel:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
In the Controller, when I do the Mapping I'm loosing the Make_ID from the Dropdownlist of the View:
public virtual ActionResult Create(CreateAdViewModel adViewModel)
{
if (ModelState.IsValid)
{
var adDto = Mapper.Map<CreateAdViewModel, CreateAdDto>(adViewModel);
_adService.CreateAd(adDto);
}
return RedirectToAction(MVC.Home.Index());
}
The mapping is:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>()
Thanks.
As you have mentionned, the Ad need to know the Model_Id and to set it into the Model
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
You also need from the other mapping side to let the Dto know where to get the Model id.
Mapper.CreateMap<Ad, CreateAdDto>()
.ForMember(dest => dest.Model_Id, opt => opt.MapFrom(src => src.Model.Id}));
The code above is not secure because a validation to see if Model is null should be added.
For the rest of your code, you seem to do it right. The section with Entity Framework requires you to attach because the entity Model already exist, otherwise, EF would insert this entity to the database.
CreateAdDto doesn't have a Make navigation property or a Make_Id property.
Solution found after some research:
ViewDomain:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
public int Model_Id { get; set; }
// Navigation properties
//public virtual ModelDto Model { get; set; }
}
Domain:
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
Viewmodel -> Dto Mapping:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>();
Dto -> Domain Mapping:
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
Atention:
To achieve this with Entity Framework, I had to attach first the Model Entity to the Context and then Ad the new Ad:
public void CreateAd(CreateAdDto adDto)
{
var adDomain = Mapper.Map<CreateAdDto, Ad>(adDto);
_modelRepository.Attach(adDomain.Model);
_adRepository.Add(adDomain);
_adRepository.Save();
}
Hope this is the best practice.
BTW I would like to have some opinions regarding this aproach.
Thanks.
Based on what is I see in your question, I suggest a simple approach. Your application is medium scale. You should very carefully about maintainability,my experience say.So try to create a simple an strain forward approach for yourself like below approach:
I can describe all layer in detail but with notice to title of your question I prefer describe only Model(bussiness Ojbect) layer:
Good! As you can see PM.Model include:
Tow sub folders contain our ViewModels and in root of Library we have a .tt file contain Entity framework Objects (POCO classes) and we have a Mapper folder(Since that i dont like use autoMapper or third party like this :) ).
You can see IListBox interface in Domain layer. I put all ListBox container to this interface.
I hope current approach useful for you but finally I suggest remove one of this layers DTO or ViewModel, because in the future will be very complex.
Good luck
Do you aware about cost of these mapping?! You have 2 layers mapping (before arrived to Entity framework) for an simple insert.We can do more complex CRUD(s) in less than 2 layers mapping.
How to think about maintainability of this code?
Please keep DRY,KISS,SOLID conventions in your mind and top of your everyday work.
Good luck

Entity Framework, two Many to Many relationship to same object using Fluent API

I am trying to define two many to many relationship to same object using fluent api.
Here is the simplified model:
public class PurchaseRequisition
{
[Key, ForeignKey("Transaction")]
public int TransactionId { get; set; }
public virtual ICollection<People> RequisitionedBys { get; set; }
public virtual ICollection<People> AuthorizedSignatures { get; set; }
}
public class People
{
[Key]
public string Id{ get; set; }
public string FullName { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForRequisitionedBys { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForAuthorizedSignatures { get; set; }
}
Here is the fluent api code:
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.RequisitionedBys)
.WithMany(b => b.PurchaseRequisitionsForRequisitionedBys)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("RequisitionedById");
x.ToTable("PurchaseRequisitionRequisitionedBy");
});
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.AuthorizedSignatures)
.WithMany(b =>b.PurchaseRequisitionsForAuthorizedSignatures)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("AuthorizedSignatureId");
x.ToTable("PurchaseRequisitionAuthorizedSignature");
});
What I want is to generate two separate linking tables, but what EF generates is two foreign key columns to PurchaseRequisition in People table and 1 foreign key column to People in PurchaseRequisition field.
Can anyone tell me what might be wrong?
The problem was fixed.
I mistakenly thought that my database initializer code would drop and recreate the database since I made changes to the model classes and my Initializer class extended DropCreateDatabaseIfModelChanges.
As Slauma suggested, fluent api code was not being reached even though the model has been changed. I was setting the initializer using SetInitializer() method and this code only ran when I used a context instance for the first time to access the DB.

DropDownlistFor Creating New Entity with MVC3

I'm developing a small app in order to better understand how MVC3 anda Razor works. I'm using MVC3, all code was generated automatically (dbContext via T4, Controller via Add Controller, Databese from EDMX model...).
In my model I have this simple model:
http://i.stack.imgur.com/nyqu4.png
public partial class Application
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ApplicationType ApplicationType { get; set; }
}
public partial class ApplicationType
{
public int Id { get; set; }
public string Type { get; set; }
}
As you can see, ApplicationType is basically an enum (shame that EF 4 has no support for enums). So, in my ApplicationController I have this:
public ActionResult Create()
{
ViewBag.AppTypes = new SelectList(db.ApplicationTypes.OrderBy(c => c.Type), "Id", "Type");
return View();
}
[HttpPost]
public ActionResult Create(Application application)
{
if (ModelState.IsValid)
{
db.Applications.Add(application);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(application);
}
And in my view:
#Html.DropDownListFor(model => model.ApplicationType.Id, (SelectList)ViewBag.AppTypes, "Choose...")
Now I'm facing two problems:
1) ApplicationType not being populated:
As #Html.DropDownListFor renders only a simple select, it fills the ID, but does not fill Type property as you can see below (sorry, I can't post images as I'm new here):
http://i.stack.imgur.com/96IR1.png
In the picture you can see that the ID is ok, but Type is empty.
What I'm doing wrong?
2) Duplicated Data
The second problem is that if I fill the Type property manually during debug (simulating a correct workflow scenario), ApplicationType is being duplicated in the database, instead of only referring to an old registry.
So, how can I make #Html.DropDownListFor refer to a previous existing item instead of creating a new one?
Thanks for your help!
I believe the mistake you're making is using your domain models in the view and assuming that on post the entire model should be completely binded and ready to store in the database. While it is possible to use domain models in the view, it's better practice to create separate View Models.
For example :
public class ApplicationViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public SelectList ApplicationTypeList { get; set; }
public string ApplicationTypeId { get; set; }
}
In your view:
#Html.DropDownListFor(model => model.ApplicationTypeId, Model.ApplicationTypeList , "Choose...")
In your controller
[HttpPost]
public ActionResult Create(ApplicationViewModel model)
{
if (ModelState.IsValid)
{
Application application = new Application()
{
Id = model.Id,
Name = model.Name,
ApplicationType = db.ApplicationTypes
.First(a => a.Id == model.ApplicationTypeId);
};
db.Applications.Add(application);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
You can then make verifying that your View Model's ApplicationTypeId corresponds to a real application type part of your modelstate's verification. You can use AutoMapper to speed up the process of converting view models to domain models.
Have you tried:
#Html.DropDownListFor(model => model.ApplicationType.Id, m => m.ApplicationType.Type, "Choose...")
Note the second parameter change.

Using enum values in model stored in a database

I don't seem to figure it out how to use enum values in an ASP.NET MVC 3 model (using code first approach) so that they are stored in a database and are preserved.
My model code looks like:
public class TestNews
{
public int Id { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
//[EnumDataType(typeof(TestNewsType))]?
public TestNewsType Type { get; set; }
[Required]
public string Title { get; set; }
}
public enum TestNewsType : short
{
Draft = 0,
PublishedPublicly = 1,
PublishedInternally = 2,
Removed = 3
}
After the database gets recreated it contains the table TestNews, but it contains only the columns:
Id
Date
Title
How to make it also store the Type in the database? And also should the EnumDataType annotation be used?
I would later like to use it in a controller action like:
public ActionResult Latest(int count=5)
{
var model = db.TestNews
.Where(n => n.Type == TestNewsType.PublishedPublicly)
.OrderByDescending(n => n.Date)
.Take(count)
.ToList();
return PartialView(model);
}
The June CTP of Entity Framework introduced Enum support.
As an alternative to using the CTP, at the MSDN blogs is an article on how to fake enums with Entity Framework.

Resources