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.
Related
I am working with some entities which are connected by foreign keys using the Entity Framework code-first approach. When I try to include one from another I get an error that says:
The expression 'user.Organization' is invalid inside an 'Include' operation, since it does not represent a property access
These are my classes:
public class User : Person
{
public StaffRole? Role { get; set; } = null;
[ForeignKey(nameof(Organization))]
public Guid? OrganizationId { get; set; }
[NotMapped]
public virtual Organization Organization { get; set; }
}
public class Organization : Auditable
{
public Organization()
{
Staffs = new List<User>();
}
[Required]
public string Name { get; set; }
public virtual ICollection<User> Staffs { get; set; }
}
I have removed some properties in order to be clear.
This is how I am trying to include
var owner = userRepository.GetAll(user => user.Id == currentUser.Id &&
user.Role == StaffRole.Owner).Include(user => user.Organization).FirstOrDefault();
I have looked around the Web to find the answer but I think I need a little bit of individual help.
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
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
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 new to Entity Framework and I have probably a simple question.
I have simplified the structure at a maximum to be clear (I hope I am).
Imagine that I just need to create a simple "Enterprise" class, with only a Name.
Then another class named "Worker" with also just a Name for the worker.
A worker should belong to an Enterprise.
An Enterprise must have a manager (who is a Worker).
So here is how I imagine these simple classes :
public class Worker
{
public int WorkerId { get; set; }
public String Name { get; set; }
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
public class Enterprise
{
public int EnterpriseId { get; set; }
public String Name { get; set; }
public Worker Manager { get; set; }
public List<Worker> Workers { get; set; }
}
I'd like these classes to result in the following DB structure :
Table Worker
WorkerId (PK, int, not null)
Name (varchar(128), not null)
EnterpriseId (FK, int)
Table Enterprise
EnterpriseId (PK, int, not null)
Name (varchar(128), not null)
Manager (FK, int)
I tried many things with modelBuilder, but I never obtain what I want.
Is there a solution with Fluent API to do what I want to do?
Thank you very much for your help.
This will not get you what you want (in Db) - but is what I recommend...
public ICollection<Worker> Workers { get; set; } // instead of List<>
// ...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalPrincipal() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
You can use it like:
var enterprise = new Enterprise { Manager = new Worker { Name = "Manager", }, };
enterprise.Workers = new[]
{
enterprise.Manager,
new Worker{ Name = "Worker1", },
new Worker{ Name = "Worker2", },
new Worker{ Name = "Worker3", },
new Worker{ Name = "Worker4", },
new Worker{ Name = "Worker5", },
};
db.Enterprises.Add(enterprise);
db.SaveChanges();
var enterprises = db.Enterprises.ToList();
This is exactly what you want...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasKey(x => x.EnterpriseId);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalDependent() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
...but will not work - due to cyclical references (EF error).
Here is a pretty detailed example for a similar / identical solution...
Entity Framework One-to-Many with Default
I don't know how you intend to get the manager object, but my guess is you need to use Inheritance to make your design optimal. Try this:
public abstract class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmployeeId{ get; set; }
public String Name { get; set; }
[ForeignKey("Enterprise"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
[Table("Workers")] // Table per Type (TPT), This will be your Table name in your database
public class Worker : Employee
{
//Add properties only related to workers
}
[Table("Managers")] // Table per Type (TPT). This will be your Table name in your database
public class Manager : Employee
{
//Add properties only related to Managers
}
public class Enterprise
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EnterpriseId { get; set; }
public String Name { get; set; }
public virtual ICollection<Employee> Employees{ get; set; }
}
Note: Sorry this is done using Property Mapping
Link: Here is a link to simple Fluent Mapping example
Link : Read about Table per Type (TPT) Inheritance here