Retrieving data in an ICollection - asp.net

Background
Since I am new to using Entity Framework, I try to build something simple first. I started a post asking how I can store lists of objects in SQL Server:
Storing list of objects in SQL Server database with code-first
Now I have built up two models:
public class MultipleChoiceQuestion
{
[Key]
public Guid MultipleChoiceQuestionId { get; set; }
[Required]
public string Question { get; set; }
[Required]
public ICollection<PossibleChoice> PossibleChoices { get; set; }
}
public class PossibleChoice
{
[Key, Column(Order = 1), ForeignKey("MultipleChoiceQuestion")]
public Guid MultipleChoiceQuestionId { get; set; }
[Key, Column(Order = 2)]
public int ChoiceIndex { get; set; }
[Required]
public string AnswerText { get; set; }
public MultipleChoiceQuestion MultipleChoiceQuestion { get; set; }
}
In QuestionContext : DbContext I have defined:
public DbSet<MultipleChoiceQuestion> McQuestions { get; set; }
Besides, I have a controller with a Get() endpoint:
[RoutePrefix("api/McQuestion")]
public class McQuestionController : ApiController
{
[AllowAnonymous]
[Route("")]
public IEnumerable<MultipleChoiceQuestion> Get()
{
var context = new QuestionContext();
return context.McQuestions;
}
}
Question
When I issue a GET request, the following object is returned.
[
{
"MultipleChoiceQuestionId": "fcaf709e-2f7d-e411-80bb-002219ac77b7",
"Question": "Which integer is a prime number?",
"PossibleChoices": null
},
{
"MultipleChoiceQuestionId": "20159ee7-2f7d-e411-80bb-002219ac77b7",
"Question": "Who is the person invented light bulbs?",
"PossibleChoices": null
}
]
How can I include the collection PossibleChoices in the GET result?

use context.McQuestions.Include("PossibleChoices").ToList();
However, you need to learn doing things the right way, so it is better to consider this:
1- Use Fluent API to map your entity to your table, you can use some tools to auto generate your entities (POCO) classes if you have already a database, check "EF 6 Tools Designer" Or "EF Reverse POCO generator".
2- Return DTOs from your Web API instead of returning the entities directly, and to map between the entity and the DTO you can use AutoMapper.

Related

EF Core one-to-many relationship with multiple contexts (databases)

I have contexts with entities like this:
public class CompanyContext : DbContext
{
public DbSet<StoreModel> Stores { get; set; }
// Other entities
}
public class DepartmentContext : DbContext
{
public DbSet<OrderModel> Orders { get; set; }
// Other entities
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> ReceivedOrders { get; set; }
public virtual ICollection<OrderModel> PreparedOrders { get; set; }
public virtual ICollection<OrderModel> IssuedOrders { get; set; }
}
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public StoreModel GettingStore { get; set; }
public StoreModel PreparingStore { get; set; }
public StoreModel IssuanceStore { get; set; }
}
For example a user makes an order in storeA, but wants to receive it in storeC, and it order will preparing in storeB. And I needs a statiscics about store received/prepared/issued orders.
When I try to create a migrations, EF throws exceptions "Unable to determine the relationship represented by navigation 'OrderModel.GettingStore' of type 'StoreModel'" and "Unable to determine the relationship represented by navigation 'StoreModel.IssuedOrders' of type 'ICollection<OrderModel>'". If I understand correctly, this happens because entities are defined in different contexts.
Now I just use next model:
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid GettingStoreId { get; set; }
public Guid PreparingStoreId { get; set; }
public Guid IssuanceStoreId { get; set; }
}
This works fine, but perhaps there are options that allow to create such a structure using navigation properties, with correct relationships between these entities from different contexts(databases).
First, the map of a different database was not placed in tables of different application formats, so think that you have a domain that should be well defined in your application, that way you would have the mapping of your application like this:
public class DomainNameContext: DbContext
{
public DomainNameContext(): base()
{
}
public DbSet<StoreModel> Stores { get; set; }
public DbSet<OrderModel> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
}
}
another thing, the relation you are using doesn't work so you can't have a repetition of Orders within the same class because this is not one -> many, this statement means that a StoreModel line can have many lines in the OrderModel this way would be like this
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid StoreModeId { get; set; } // this part will show the entity framework that this is the fk it will correlate
public StoreModel StoreModel { get; set; }
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> OrderModels { get; set; }
}
see that if you are wanting to have many StoreModel related to many OrderModel then you need to use many -> many which microsoft documentation foresees to use as well
good to map this within its context it is necessary in OnModelCreating to use its mapping like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
modelBuilder.Entity<StoreModel>()
.HasMany<OrderModel>(g => g.OrderModels )
.HasForeignkey<Guid>(s => s.StoreModeId )
}
you can have a look at the microsoft documentation enter link description here, enter link description here
now if you need to map between contexts you will have to use dapper to make separate queries in separate bases the entity has support for that in this link enter link description here
and then you can make the necessary inner joins so that you can use it but natively this does not exist, I advise you to rethink your database so that it can make more sense to a relational model, perhaps putting types for your StoreModel and OrderModel so you can use the way I wanted the types GettingStore, PreparingStore, IssuanceStore using an enum for this to make it explicit

ASP.NET MVC Auto generate integer number

Good day, a really newbie developer here.
I Have a form and it have a entity of "QueueNumber" Can someone show me how to code so that when ever i save my form it generates automatically QueueNumber + the Prefix, btw my prefix entity is in another class
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
public string QueueNumber
public int ServiceId { get; set; }
public Service Service { get; set; }
}
-
public class Service
{
public int ServiceId { get; set; }
[Display(Name = "Service Name")]
public string ServiceName { get; set; }
[Display(Name = "Service Letter")]
public string ServiceLetter { get; set; }
[Display(Name = "Status")]
public bool? Status { get; set; }
[Display(Name = "Assigned Location")]
public int? LocationId { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<Customer> Customer { get; set; }
}
Outcome in database :
1. A001
2. A002
3. A003
i just want to be able to generate a queue number automatically and when i save in data base its like A= Service Letter and 001=QueueNumber. Thankyou
If the QueueNumber needs to be persisted to the table, then I would set it up as a calculated column so that the database can manage computing it and updating it if the underlying fields change.
If it is just something that you want to represent in the UI then I would recommend having the view model calculate this.
The entity can calculate something like this with a [NotMapped] attribute. For example:
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
[NotMapped]
public string QueueNumber
{
get { return string.Format("{0}{1:000}", Service?.ServiceLetter ?? "?", QueueId);
}
[ForeignKey("Service")]
public int ServiceId { get; set; }
public Service Service { get; set; }
}
The problem with this approach is that to be able to rely on your Queue to reveal a QueueNumber, the Queue must eager load the Service, or you enable lazy loading and risk that performance hit vs. having Service == #null and getting an exception or invalid QueueNumber result. In the above example, if the Service isn't eager loaded you will get back something like "?001".
I prefer to use ViewModels for a number of reasons including performance, security, and handling conditions like this more cleanly.
For example, given a QueueViewModel as such:
[Serializable]
public sealed class QueueViewModel
{
public int QueueId{ get; set; }
public string Name { get; set; }
public string ServiceName { get; set; }
public string ServiceLetter { get; set; }
public string QueueNumber
{
return string.Format("{0}{1:000}", ServiceLetter, QueueId);
}
}
Then when reading the data, we don't pass Entities to the view, we pass our view model...
var viewModel = context.Queues
.Where(x => x.QueueId == queueId)
.Select(x => new QueueViewModel
{
QueueId = x.QueueId,
Name = x.Name,
ServiceName = x.Service.Name,
ServiceLetter = x.Service.ServiceLetter
}).Single();
return viewModel;
The benefits of this approach:
We don't have to worry about eager/lazy loading. The query fetches everything needed, and our view model can compute anything needed from the data loaded. (Queries can compute values as well if you like, but be wary of limitations in that the query has to be able to go to SQL, so no user functions, etc.)
Performance is improved since the query only returns the data needed rather than entire entity graphs, and no rish of lazy load hits.
Security is improved, we expose no more data to the client than is expected/needed, and we don't open the door for "lazy" updates where entities are attached to a context and saved without proper validation.

Entity Framework shows inconsistent behaviour when used with Asp.net Identity

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.
In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.
If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

OData and Entity Framework different Models mapping

I have a class used by Entity Framwork, called EFApplication.
Following this link, i want to add OData query option to my Web Api controller.
However, i don't want to use EFApplication class as the query parameters.
I want to use another class used just for the OData querying (ODataApplication)
Is this possible?
One of the reasons i want to use a different class is because OData doesn't support DateTime properties, and this means that i need to change it to DateTimeOffset, which i cannot do it (without breaking many other things)
// class mapped to Entity Frameowrk / Database
public class EFApplication
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime DateCreated { get; set; }
}
// class used for OData querying only
public class ODataApplication
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ApplicationsController : ApiController
{
[EnableQuery]
public IQueryable<EFApplication> Get(ODataQueryOptions<ODataApplication> options)
{
// how to apply ODataQueryOptions<ODataApplication> query on top of EFApplication
return result; // IEnumerable<EFApplication>
}
}
You can fix this problem with Automapper and its feature Project().To()
// ViewModel class mapped to Entity Framework Model class
public sealed class EFApplicationViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTimeOffset DateCreated { get; set; }
}
In your Controller for sample:
[EnableQuery]
public IQueryable<EFApplication> Get(ODataQueryOptions<ODataApplication> options)
{
IQueryable<EFApplication> queryable = EFApplicationRepository.All();
IQueryable<EFApplicationViewModel> projected = queryable.Project().To<EFApplicationViewModel>();
return projected;
}
In addition into Automapper you should set a little 'fix' for the DatetimeOffset(again sigh...)
Mapper.CreateMap<EFApplication, EFApplicationViewModel>();
Mapper.CreateMap<DateTime, DateTimeOffset>().ProjectUsing(src => src);
Mapper.CreateMap<DateTime, DateTimeOffset>().ConvertUsing(src => DateTime.SpecifyKind(src, DateTimeKind.Utc));
If you block with DateTime, now it's not a problem.
Here's latest release of Web API OData 5.4 RC.
The release note is
http://odata.github.io/WebApi/5.4-rc/
And here's a basic sample about the DateTime Support in Web API OData V4

Create Dapper Repository Method for Complex Data

I want to create a generic repository method for the complex data(Data Result of multiple joins in database). Following are the classes which hold the data . The data comes from SQL is the join of three tables(Tables architecture is same as of class)
public class InterfaceHeaderSetting
{
public Guid Id { get; set; }
public string CodaDocCode { get; set; }
public string Company { get; set; }
public string Currency { get; set; }
public string DocDescription { get; set; }
public Screen Screen { get; set; }
public Interface Interface { get; set; }
}
public class Screen
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Interface
{
public Guid Id { get; set; }
public string Name { get; set; }
}
I have the repository methods like
public IEnumerable<T> GetAllDynamic(string sql)
{
return Connection.Query<T>(sql, commandType: CommandType.StoredProcedure).ToList();
}
public T Update(T entity, string sql, object dynamicParameters)
{
return
Connection.Query<T>(sql, dynamicParameters, commandType: CommandType.StoredProcedure).SingleOrDefault();
}
I want one more repository method by which I can fill the objects like InterfaceHeaderSetting object.
I don't think you should reinvent the wheel. For this type of function i think Entity Framework is the solution.
In my projects i let entity framework handle more advanced querys that need joins and let dapper do the simple insert, update and select jobs.

Resources