Business Logic Architecture with Entity Framework - asp.net

I`m using Entity Framework and I have entities like this:
public class User : IEntity
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required]
public String Email { get; set; }
public virtual ICollection<Project> UserProjects { get; set; }
}
public class Project : IEntity
{
[Key]
public int ProjectId { get; set; }
public String Title { get; set; }
public String Description { get; set; }
[ForeignKey("UserOwner")]
public int UserOwnerId { get; set; }
public virtual User UserOwner { get; set; }
}
Also I use Repository pattern and Unit Of Work pattern.
For example I have method
CreateProject(String title, String description, String userOwnerEmail)
in Projects logic class which contains only Project Repository.
Also i have UserLogic class that allows me get user by his email.
How i can get user by his email in CreateProject method to designate him like a project owner.
The main aim is to create loose coupling method.
I think that this example is bad:
public void CreateNewProject(String projectName, String description,String usersEmail)
{
var usersLogic = kernel.Get<IUsersServices>();
User owner = usersLogic.GetUserByEmail(usersEmail);
unit.Repository<Project>()
.Insert(new Project
{
Title = projectName,
Description = description,
CreationDate = DateTime.Now,
UserOwner = owner,
UsersIncludeedInProject = new List<User>()
});
unit.Save();
}

Business Logic and Entity Framework (or any other ORM) don't belong in the same phrase. Separation of Concerns is the principle.
class Project
{
public Project(IProjectRepository repo, IUsersServices userServ){}
public void CreateNewProject(String projectName, String description,String usersEmail)
{
var owner=_users.GetByEmail(usersEmail);
//create project\\
_repository.Save(project);
}
One other approach is to pass the User object as an argument, object you'll get by asking a UserService or even a UserRepository to GetUserByEmail(). But it will be outside the CreateNewProject method
var user= _usersService.GetByEmail();
var project=project.CreateNewProject(projectName,projectDescription,user);
_projectRepository.Save(project);
In this case CreateNewProject does exactly that, because it's probably not its concern to save the project. I recommend this second approach.

Related

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

XMLserializer, Entity Framework : Cannot serialize member of type ICollection see inner exception for more details

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; }
}

How can I create two types of users in MVC5?

I'm creating MVC5 app, and I'm already using ASP.NET Identity to create users. So, I already have the AspNetUsers table, and whenever user registers I get an entry there. I also have an Admin role, where I manually specify, which registered user is an admin. On the other hand, I also need to register Businesses, and much like normal Users, they will be able to log-in, register, and do some stuff. The point is that they will have both some similar and different fields with/from the normal users. For example, they will also have, e-mail address, password (which I want to be hashed like for normal users), e-mail confirmation, unique id etc. But they have different fields for more information, like their address, zip, country, category, etc. which normal users don't have. How can I achieve this in MVC?
Should I do something like the ApplicationUser class?
public class ApplicationUser : IdentityUser
I mean, should I inherit my Business model from the IdendityUser? If yes, how will my model know which of the fields from IdentityUser to use and which not?
Here is my current Business model:
public class Business
{
public int BusinessID { get; set; }
public string BusinessName { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Subcategory")]
public int SubcategoryID { get; set; }
public virtual Subcategory Subcategory { get; set; }
public string BusinessAddress { get; set; }
public string BusinessZip { get; set; }
public string BusinessPhone { get; set; }
public string BusinessDescription { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
[Range(0.0, 5.0)]
public double BusinessRating { get; set; }
public virtual ICollection<Review> Reviews { get; set; }
}
So, apart from those fields, I want my table to include the stuff similar to AspNetUsers, like Email, EmailConfirmed, PasswordHash, SecurityStamp, etc.
EDIT:
Please note that some of my fields in the Business model are required. And also below you can find my ApplicationUser class.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
Use simple inheritance:
public class Business : ApplicationUser
{
...
}
You'll end up with a Discriminator column in your AspNetUsers table that will help Entity Framework identity which class it should instantiate for the row (Business or ApplicationUser). Then you can either just query as normal or if you only want one particular type or another, you can use OfType<T>:
var businessUsers = db.Users.OfType<Business>();
Note: By default, Entity Framework handles simple inheritance with a single table with a Discriminator column. For most cases this works just fine, but you must keep in mind that any property you add to subclasses of your base class, must be nullable. You cannot require something like a DateTime on Business to be required at the database-level, because then you could never save an ApplicationUser, which does not that property. However, this is only an issue at the database-level. You can still use view models to make a particular property on Business required from a front-end perspective.

Why do the ASP.NET Identity interfaces use strings for primary and foreign keys?

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();
}

Resources