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
Related
I want to map XML elements into my database table (using Entity Framework):
var xmlSerializer = new XmlSerializer(typeof(Participant), new XmlRootAttribute("participant"));
var participant = (Participant)xmlSerializer.Deserialize(new StringReader(content));
I have Participant table which I can access by
[XmlRoot("participant", Namespace = "")]
public partial class Participant
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Participant()
{
this.GroupParticipant = new HashSet<GroupParticipant>();
this.ParticipantAddress = new HashSet<ParticipantAddress>();
this.ParticipantPublisher = new HashSet<ParticipantPublisher>();
this.ParticipantJob = new HashSet<ParticipantJob>();
this.ParticipantProvider = new HashSet<ParticipantProvider>();
}
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
//[XmlElement("address")]
//[XmlElement("address")]
//[XmlArray("HashSet<ParticipantAddress>"), XmlElement("address")]
//[XmlArrayItem("ICollection<ParticipantAddress>")]
//[XmlAttribute(DataType = "ICollection<ParticipantAddress>", AttributeName = "address")]
[XmlElement("address", typeof(List<ParticipantAddress>))]
public virtual ICollection<ParticipantAddress> ParticipantAddress { get; set; }
}
ParticipantAddress is ICollection:
[Serializable]
[XmlInclude(typeof(HashSet<ParticipantAddress>))]
public partial class ParticipantAddress
{
public int ParticipantAddressId { get; set; }
public int ParticipantId { get; set; }
[XmlElement("city")]
public string City { get; set; }
[XmlElement("state")]
public string State { get; set; }
[XmlElement("zipCode")]
public string ZipCode { get; set; }
public virtual Participant Participant { get; set; }
}
Exception says:
{"There was an error reflecting type 'x.Participant'."}
My inner Exception says:
{"Cannot serialize member 'xParticipant.ParticipantAddress' of type 'System.Collections.Generic.ICollection`1[[x.ParticipantAddress, APS.Data.BatchInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details."}
I am reading XML by streamReader.
I have tried
[XMLArray]
Changing ICollection to List
make class serializable
Is there any other way to overcome this problem or any examples related to my question or any changes I need to implement in my code?
ICollection is not serializable.
- You can use DTO.
- You can change the collection type (i.e. with List<>) and with XML serialization attributes avoid circular references and/or disable lazy load (i.e. use eagerly load using Include method) or the risk is that you serialize the whole database.
You have this issue because of the virtual properties. You try to serialize a class which has a reference to another class, which has a reference to the first, class, which... endless loop.
If you want to serialize an entity, the best thing you can do is use a DTO class, which is a class used only to export your data. In these classes you can't have virtual properties, but what you can do is include the DTO objects of your ParticipantAddress.
The other thing you can try, if it isn't a necessity to serialize to XML, is use the Newtonsoft.Json package to serialize the entities. The package has some options to deal with navigational properties.
I have created a region and change ICollection<> to List<> because
ICollection is an interface and interfaces are not serializable.
But List<> is a class and this class implements all the below interfaces:
IList, ICollection, IList, ICollection, IReadOnlyList, IReadOnlyCollection, IEnumerable, IEnumerable.
I kept both Icollection as well as List and put [XmlIgnore] on ICollection.
[XmlRoot("participant", Namespace = "")]
public partial class Participant
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Participant()
{
this.GroupParticipantList = new List<GroupParticipant>();
this.ParticipantAddressList = new List<ParticipantAddress>();
this.ParticipantPublisherList = new List<ParticipantPublisher>();
this.ParticipantJobList = new List<ParticipantJob>();
this.ParticipantProviderList = new List<ParticipantProvider>();
}
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
[XmlIgnore]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ParticipantAddress> ParticipantAddress { get; set; }
#region Custom properties
[XmlElement("address")]
public virtual List<ParticipantAddress> ParticipantAddressList { get; set; }
#endregion
}
But with this option I am having another Problem: If I do any single change in my SQL database and if I do update model from database, then I lose manually implemented code like all the XML sentences in this code.
I answered this in the below article to add [System.Xml.Serialization.XmlIgnore] to entity.tt template
Prevent Property from being serialized
I had a similar issue using EF, to implement a Web service and couldn't serialize the ICollection<> object.
I hopes this helps you out.
public class User
{
public User()
{
sessions = new HashSet<Session>();
}
public int Id { get; set; }
public string Name { get; set; }
[XmlIgnore]
[IgnoreDataMember]
public virtual ICollection<Session> sessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public Datetime start_dtime{ get; set; }
public Datetime end_dtime{ get; set; }
public virtual User user{ get; set; }
}
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.
I'm looking at the interfaces on the new ASP.NET Identity classes and the database it creates using Entity Framework Code First. I'm using the Visual Studio 2013 RC.
At first glance the database schema looks reasonably normal:
But all the keys are NVARCHAR(128)
And for some crazy reason AspNetUserSecrets.Id is a PK that looks like it could point to more than one record in the AspNetUsers table. Does this mean multiple AspNetUsers will have to share the same password?
When I look at the Looking at the interfaces you're forced to implement, these are all strings...
public class User : IUser
{
public string Id { get; set; }
public string UserName { get; set; }
}
public class UserSecret : IUserSecret
{
public string UserName { get; set; }
public string Secret { get; set; }
}
public class UserRole : IUserRole
{
public string UserId { get; set; }
public string RoleId { get; set; }
}
public class UserClaim : IUserClaim
{
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class UserManagement : IUserManagement
{
public string UserId { get; set; }
public bool DisableSignIn { get; set; }
public DateTime LastSignInTimeUtc { get; set; }
}
public class Tokens : IToken
{
public string Id { get; set; }
public string Value { get; set; }
public DateTime ValidUntilUtc { get; set; }
}
public class UserLogin : IUserLogin
{
public string UserId { get; set; }
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
public class Role : IRole
{
public string Id { get; set; }
public string Name { get; set; }
}
So I'm coming to terms with the fact that I may have to implement this using strings for PK and FK relationships.
But I'd really love to know WHY it's built like this...?
EDIT: Time has passed and there are now articles on how to extend the asp.net identity to use int (or guid) fields:
http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
The intent was to allow both arbitrary id types (i.e. int, guid, string), but also avoid having serialization/casting issues for the id property.
So you can define your keys however you like and just implement the interface method
public class MyUser : IUser {
public int Id { get; set; }
string IUser.Id { get { return Id.ToString(); } }
}
Adding to what Hao said:
The Identity runtime prefers strings for the user ID because we don’t want to be in the business of figuring out proper serialization of the user IDs (we use strings for claims as well for the same reason), e.g. all (or most) of the Identity interfaces refer to user ID as a string.
People that customize the persistence layer, e.g. the entity types, can choose whatever type they want for keys, but then they own providing us with a string representation of the keys.
By default we use the string representation of GUIDs for each new user, but that is just because it provides a very easy way for us to automatically generate unique IDs.
With ASP.NET Core, you have a very simple way to specify the data type you want for Identity's models.
First step, override identity classes from < string> to < data type you want> :
public class ApplicationUser : IdentityUser<Guid>
{
}
public class ApplicationRole : IdentityRole<Guid>
{
}
Declare your database context, using your classes and the data type you want :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
And in your startup class, declare the identity service using your models and declare the data type you want for the primary keys :
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders();
In ASP.NET identity tables, primary keys will still be in NVARCHAR but in your application it's will be the data type you want.
You can check this in a controller :
[HttpGet]
public async Task<IActionResult> Test()
{
ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
Guid userId = user.Id; // No cast from string, it's a Guid data type
throw new NotImplementedException();
}
I have a Windows Form project that I would like to migrate toward a web application using ASP.NET MVC2.
In this project I have some POCO classes as in this example that are part of a class library and that I would like to use with a binary reference
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
public DateTime BornDate { get; set; }
...
}
Is there a way to use these classes inside my Web MVC project and adding, for example validation attributes without modifying the original assembly?
thanks for helping
You may take a look at FluentValidation. It integrates nicely with ASP.NET MVC and allows you to unobtrusively add validation rules without modifying your POCO objects.
You can add Meta Information like Validation by using a Partial Class
namespace xxx.Data.yyy
{
[MetadataType(typeof(Posting_Validation))]
public partial class Posting {
}
public class Posting_Validation {
[Required(ErrorMessage = "Need title")]
[StringLength(50, ErrorMessage = "Must be under 50 characters")]
[DisplayName("Title")]
public string Title { get; set; }
[Display(AutoGenerateField = false)]
[HiddenInput(DisplayValue=false)]
public int PostingId { get; set; }
[UIHint("tiny_mce")]
public string HtmlContent { get; set; }
}
}
I've used Fluent NH in my project but I'm having some problems with using the Collection class. Here's the code for my classes
public class Vendor
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Services Services { get; set; }
}
public class Services : IList<Service>
{
}
public class Service
{
int id{ get; set; }
int Code { get; set; }
}
this instead of put service as list in the vendor class
public virtual IList<Service> Services { get; set; }
I want to use services collection class.
and the mapping code
public class VendorMap : ClassMap<Vendor>
{
public VendorMap()
{
Table("Vendor");
Id(x => x.Id);
Map(x => x.Name);
HasMany<Service>(x => x.Services)
.KeyColumn("Vendor_Id")
.CollectionType<Services>()
.Not.LazyLoad();
}
I got this error "Custom type does not implement UserCollectionType: Services"
Any ideas on how to map this?
Thanks in advance.
Try this :
HasMany(x => x.Services)
.KeyColumn("Vendor_Id")
.AsBag()
.Cascade.All()
.Not.LazyLoad();
It works great for me!
NHibernate does not permit mapping collection classes of this type. They must be an interface, like IList<T>, as NHibernate provides it's own implementation.
This implementation obviously does not meet the interface of the Services class, so NHibernate is unable to map it.