I work on a EF Core project for WPF app.
We decide to split the DbContext in 2 smaller Dbcontexts: (the project contains a single Database)
public class FirstDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DBSet<Parameters>{ get; set; }
}
public class SecondDbContext: DbContext
{
public DBSet<User>{ get; set; }
public DBSet<Books> { get; set; }
public DBSet<Parameters>{ get; set; }
}
and we keep a "super" DbContext (which contains all the DbSets from the DB) to maintain and migrate the DB
public class SuperDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DBSet<User>{ get; set; }
public DBSet<Books> { get; set; }
public DBSet<Parameters>{ get; set; }
}....
The first step to refactor code is to replace the lines which references the "SuperDbContext..." with the correct call "SecondDbContext.Books..."or "FirstDbContext.Post"... OK
Question:
In the Client app, the choice of DbContext is depending about a user's choice when app is launching: if user choose option1 => FirstDbContext, and if option2 => SecondDbContext.
How can we write the code to switch on the current DbContext to manage the "common DbSet" (Parameters) : before refactoring we have for example:
SuperDbContext.Parameters.FirstOrDefault()...
and now? do we have to write something like this:
if(option1)
{
FirstDbContext.Parameters.First()
}else
{
SecondDbContext.Parameters.First()
}
And what is the impact in the repositories? cause if we maintain this approach we have to duplicate code in the 2 Repositories?
No no... Forget about the approach you mentioned, you'll end up writing twice the code you need. Why don't you just use db context type :
At the beginning of your function, a single if else :
DbContext context;
if(option1)
{
context = new firstContextEntities();
}
else
{
context = new secondContextEntities();
}
And as both of your contexts are almost exactly the same, you will use your context by casting it to the greater one (the one that has the most access) :
var FirstLine = ((secondContextEntities)context).Parameters.First();
Related
I'm trying to make a relationship between the Users from the table generated by Asp.Net Identity with my own table. The relationship must be many to many, since many Users can work on the same Task (which is my table), and same time an User can work on multiple Tasks.
public class Task
{
public int ID { get; set; }
public string Name { get; set; }
public string UserID { get; set; }
public virtual ICollection<ApplicationUser> Users { get; set; }
}
public class ApplicationUser : IdentityUser
{
public int TaskID { get; set; }
public virtual ICollection<Task> Tasks{ get; set; }
// rest of the code
}
I try it this way but I get an error during migration (or run time)
"One or more validation errors were detected during model generation:"
Please help me solve this problem and archive what I need.
Try it like this:
public class Projects
{
public Projects()
{
ApplicationUser = new HashSet<ApplicationUser>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
Application User
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Projects = new HashSet<Projects>();
}
public async Task GenerateUserIdentityAsync(UserManager 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;
}
public virtual ICollection <Projects > Projects { get; set; }
}
Application Context :
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public virtual DbSet<Projects> Projects { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
now when I run this Mvc app and register, the db tables I get is like the following:
and the correct schema:
The things to be questioned are a lot, from my point of view important is to determine if you:
- can/should you mix application context and your model context ?
You can try it as shown below using Fluent API.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Task>()
.HasMany<ApplicationUser>(s => s.Users)
.WithMany(c => c.Tasks)
.Map(cs =>
{
cs.MapLeftKey("TaskRefId");
cs.MapRightKey("ApplicationUserRefId");
cs.ToTable("TaskApplicationUser");
});
}
Update : you can see this link too.
EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType
Error text is not related to your many-to-many relationship. It tips that other built-in entities are not configured properly. So, It would be nice if you provided full definition of your custom DbContext-class and how it is configured.
UPDATE
As i understood u are working with two different contexts. You must work with the same context, cause of u are extending IdentityContext, creating relationships and adding custom types. So problem then will be resolved itself.
Hope, this will help.
I have read and seen few projects that talks about splitting or multiple DbContext and I'm not sure what is the best practice.. should I create each DbContext for each entity or have all entity in one DbContext?
this is what I have for now.
public class PersonsContext : DbContext
{
public PersonsContext() : base("name=EmployeeContext")
{
}
public DbSet<Person> People { get; set; }
}
public class OrderContext : DbContext
{
public OrderContext() : base("name=EmployeeContext")
{
}
public DbSet<Order> People { get; set; }
}
Actually you don't need have to create one DbContext for each Entity. But you can have multiple DbContext for a single database. For an example let's say you want to separate your business aspect and security aspect of the application into two different modules. Then of course you can have two different context such as SecurityContext which has all the entities related to Security and BusinessContext consists of Business related entities.
Ex:
public class SecurityContext : DbContext{
public PersonsContext() : base("name=securitycontext"){
}
public DbSet<User> User { get; set; }
public DbSet<Role> Role { get; set; }
public DbSet<Group> Group { get; set; }
}
public class BusinessContext : DbContext{
public OrderContext() : base("name=businesscontext"){
}
public DbSet<Order> Order { get; set; }
public DbSet<OrderLine> OrderLine { get; set; }
public DbSet<Customer> Customer { get; set; }
}
I am trying to create the following constraint in my model so that a Tag object's TagType is valid. A valid TagType is one whose OperatingCompanyId matches the Tag's Website's OperatingCompanyId. I realize that this seems convoluted however it makes sense from a business standpoint:
An Operating Company has WebSites. Websites contain Tags. Tags have a TagType(singular). TagTypes are the same across Operating Companies, meaning that if one Operating Company has twenty TagTypes and five WebSites, those twenty TagTypes should be able to be used across all fives of those WebSites. I want to ensure that a Tag's TagType cannot be one associated with another OperatingCompany.
What is the best way to create this constraint in the model? Do I need to change my POCO, or use the Fluent API?
Thanks in advance!
[Table("OperatingCompanies")]
public class OperatingCompany : ConfigObject
{
public OperatingCompany()
{
WebSites = new List<WebSite>();
}
[Required(ErrorMessage = "Name is a required field for an operating company.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters.")]
public string Name { get; set; }
public virtual ICollection<WebSite> WebSites { get; set; }
}
[Table("Websites")]
public class WebSite : ConfigObject
{
public WebSite()
{
WebObjects = new List<WebObject>();
}
[Required(ErrorMessage = "URL is a required field for a web site.")]
[MaxLength(100, ErrorMessage = "URL cannot exceed 100 characters for a web site.")]
[RegularExpression(#"\b(https?|ftp|file)://[-A-Za-z0-9+&##/%?=~_|!:,.;]*[-A-Za-z0-9+&##/%=~_|]", ErrorMessage = "The value entered is not a valid URL.")]
public string Url { get; set; }
public OperatingCompany OperatingCompany { get; set; }
[Required(ErrorMessage = "You must associate a web site with an operating company.")]
public Guid OperatingCompanyId { get; set; }
[InverseProperty("Website")]
public virtual ICollection<WebObject> WebObjects { get; set; }
}
[Table("Tags")]
public class Tag : ConfigObject
{
[Required(ErrorMessage = "Name is a required field for a tag.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag.")]
public string Name { get; set; }
public TagType TagType { get; set; }
[Required(ErrorMessage = "You must associate a tag with a tag type.")]
public Guid TagTypeId { get; set; }
public WebSite WebSite { get; set; }
[Required(ErrorMessage = "You must associate a tag with a web site.")]
public Guid WebSiteId { get; set; }
}
[Table("TagTypes")]
public class TagType : ConfigObject
{
[Required(ErrorMessage = "Name is a required field for a tag.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag type.")]
public string Name { get; set; }
public OperatingCompany OperatingCompany { get; set; }
[Required(ErrorMessage = "You must associate a tag type with an operating company.")]
public Guid OperatingCompanyId { get; set; }
}
One way to enforce this constraint is to take advantage of the new validation feature introduced as part of new DbContext API in EF 4.1. You can write a custom validation rule to make sure that tag types for any given company's website are selected from the valid tag types for that company. The following shows how it can be done:
public abstract class ConfigObject
{
public Guid Id { get; set; }
}
public class OperatingCompany : ConfigObject, IValidatableObject
{
public string Name { get; set; }
public virtual ICollection<WebSite> WebSites { get; set; }
public virtual List<TagType> TagTypes { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var allTagTypes = (from w in WebSites from t in w.Tags select t.TagType);
if (!allTagTypes.All(wtt => TagTypes.Exists(tt => tt.Id == wtt.Id)))
{
yield return new ValidationResult("One or more of the website's tag types don't belong to this company");
}
}
}
public class WebSite : ConfigObject
{
public string Url { get; set; }
public Guid OperatingCompanyId { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public OperatingCompany OperatingCompany { get; set; }
}
public class Tag : ConfigObject
{
public string Name { get; set; }
public Guid TagTypeId { get; set; }
public Guid WebSiteId { get; set; }
public TagType TagType { get; set; }
public WebSite WebSite { get; set; }
}
public class TagType : ConfigObject
{
public string Name { get; set; }
public Guid OperatingCompanyId { get; set; }
public OperatingCompany OperatingCompany { get; set; }
}
public class Context : DbContext
{
public DbSet<OperatingCompany> OperatingCompanies { get; set; }
public DbSet<WebSite> WebSites { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<TagType> TagTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>().HasRequired(t => t.WebSite)
.WithMany(w => w.Tags)
.HasForeignKey(t => t.WebSiteId)
.WillCascadeOnDelete(false);
}
}
As a result, EF will invoke that validate method each time you call DbContext.SaveChanges() to save an OperatingCompany object into database and EF will throw (and abort the transaction) if the method yields back any validation error. You can also proactively check for validation errors by calling the GetValidationErrors method on the DbContext class to retrieve a list of validation errors within the model objects you are working with.
It also worth noting that since you use your domain model as also a View Model for your MVC layer, MVC will recognize and honor this Validation rule and you can check for the validation result by looking into the ModelState in the controller. So it really get checked in two places, once in your presentation layer by MVC and once in the back end by EF.
Hope this helps.
however... if I understand the purpose of MVC / EF it is to have that
business logic inside of the Model...
And what model do you mean? If you take ASP.NET MVC and EF you will end with three areas which are sometimes called model:
EF model - that is set of classes with their mapping to database
Model-View-Controller - model here means something (usually business logic) consumed by your controller to prepare data for view
View model - In ASP.NET MVC view model is class with data exchanged between controller and view
If I look at your classes I see first and third model coupled together (most of the time this is considered as a bad practice). Your understanding is correct but mostly in terms of second model which is not represented by your classes. Not every "business logic" can be represented by mapping. Moreover it is not a point of data layer to do business logic.
Your mapping partially works (tag type is related only to one operating company) but still your data layer doesn't enforce all your business rules. Data layer still allows web site to have assigned tag with tag type from different operating company and your business logic must ensure that this will not happen. Avoiding this in database would be complicated because it would probably require complex primary keys and passing operating company Id to every dependent object.
If I were you, I will use business layer to filter Tagtype instead of do such constraint in database. For me that approach may be easier.
I am developing an piece of software where I have a few entities such as:
public class Workspace
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Playground> Playground { get; set; }
public virtual List<Workspace> Children { get; set; }
public virtual List<Member> Members { get; set; }
public virtual Workspace Parent { get; set; }
}
public class Playground
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Service> Services { get; set; }
public virtual Workspace Workspace { get; set; }
}
public class Service
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual Playground Playground { get; set; }
}
These are my EF4 POCO objects. I am using the repository pattern and the following interface:
public interface IRepository<T>
{
void Add(T entity);
void Delete(T entity);
IEnumerable<T> Get(Expression<Func<T, bool>> expression);
IEnumerable<T> Get();
void Attach(T entity);
int Save();
}
The repositories have an internal ObjectContext. I have a UnitOfWork that contains instances of my repositories and is responsible to save the changes made to them.
Am i doing it right so far?
I am implementing a Business Logic Layer like this:
public class DomainWorkspaceService : DomainServiceBase
{
public DomainWorkspaceService(Workspace workspace)
: base(UnitOfWorkFactory.GetInstance())
{
}
public void Create(Workspace workspace)
{
UoW.GetRepository<Workspace>().Add(workspace);
}
public void Delete(Workspace workspace)
{
var pservice = new DomainPlaygroundService();
foreach (var playground in workspace.Playground)
pservice.Delete(playground);
foreach (var child in workspace.Children)
Delete(child);
}
}
Now i'm not sure i am going in the right direction. My POCOs are (will be) responsible for validation and will enable me to do something like
SomeWorkspace.Children.Add(new Workspace {...});
Since these objects are associated with a context, when i save them will the changes to the collections also be saved in the database?
Also, I want that my Playgrounds can't be created without a Workspace and Services without a Playground. Where should i create and delete them?
Thanks.
So far, so good.
You probably want to move your Save method off of the Repository and onto the unit of work. In the Entity Framework, you have to save all changes in the context at once; you cannot do it per-type. Putting it on the Repository implies that only changes to the repository will be saved, which is probably not correct.
You might want to implement the cascade from workspace to playground as database cascades (which the Entity Framework will recognize and support) instead of manually coding them.
Yes, saving changes to the context will save all tracked changes, including related objects.
Regarding adding, if you don't expose the ObjectContext, then the only way that anyone will be able to add a Playground is via relationships with the objects you do expose.
I'm producing a simple composite patterned entity model using EF4 w/ the code-first CTP feature:
public abstract partial class CacheEntity
{
[Key]public string Hash { get; set; }
public string Creator { get; set; }
public int EntityType { get; set; }
public string Name { get; set; }
public string Predecessor { get; set; }
public DateTime DateTimeCreated { get; set; }
public virtual ICollection<CacheReference> References { get; set; }
}
public partial class CacheBlob : CacheEntity
{
public byte[] Content { get; set; }
}
public partial class CacheCollection : CacheEntity
{
public virtual ICollection<CacheEntity> Children { get; set; }
}
public class CacheReference
{
public string Hash { get; set; }
[Key]public string Reference { get; set; }
public virtual CacheEntity Entity { get; set; }
}
public class CacheEntities : DbContext
{
public DbSet<CacheEntity> Entities { get; set; }
public DbSet<CacheReference> References { get; set; }
}
Before I split out the primitive/collection derived classes it all worked nicely, but now I get this:
Unable to determine the principal end of the 'Cache.DataAccess.CacheEntity_References'
relationship. Multiple added entities may have the same primary key.
I figured that it may have been getting confused, so I thought I'd spell it out explicitly using the fluent interface, rather than the DataAnnotation attributes. Here's what I think defines the relationship properly:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CacheEntity>().HasKey(ce => ce.Hash);
modelBuilder.Entity<CacheEntity>().HasOptional(ce => ce.References).WithMany();
modelBuilder.Entity<CacheReference>().HasKey(ce => ce.Reference);
modelBuilder.Entity<CacheReference>().HasRequired(cr => cr.Entity).WithOptional();
}
But I must be wrong, because now I get this:
Entities in 'CacheEntities.CacheReferenceSet' participate in the
'CacheReference_Entity' relationship. 0 related 'Entity' were found. 1 'Entity' is expected.
Various other ways of using the fluent API yield different errors, but nothing succeeds, so I am beginning to wonder whether these need to be done differently when I am using inheritance.
Any clues, links, ideas, guidance would be very welcome.
using the MapHierarchy works for me:
protected override void OnModelCreating(ModelBuilder builder){
builder.Entity<CacheBlob>().HasKey(b=> b.Hash).MapHierarchy();
}
As an example.
Further reference : http://blogs.msdn.com/b/efdesign/archive/2009/10/12/code-only-further-enhancements.aspx