I'm using Entity Framework's Database First approach. I would like to have multiple companies in one database and for that I would like to be able to override table name by adding a prefix in front of it (i.e. Company1$Users, Company2$Users) when creating DBContext. It all works fine the first time until I change the company. It seems that DBContext caches entities and it does not fire OnModelCreating next time when I create the context. I have been searching for a solution almost everywhere but cannot seem to find any. Am I the only one having this issue? Is it actually possible? Can someone please help me with this?
The code to recreate the issue is below:
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
}
public class EntityMappingContext : DbContext
{
public String CompanyId { get; set; }
public DbSet<User> Users { get; set; }
public EntityMappingContext(string companyId = null) : base("DB")
{
CompanyId = companyId;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable(CompanyId + "$Users");
base.OnModelCreating(modelBuilder);
}
}
To recreate the issue you can run the following:
using (var context = new EntityMappingContext("Company1"))
{
foreach (var user in context.Users)
{
Console.WriteLine(user.FirstName + " " + user.LastName);
}
}
using (var context = new EntityMappingContext("Company2"))
{
foreach (var user in context.Users)
{
Console.WriteLine(user.FirstName + " " + user.LastName);
}
}
The first and second context queries Company1$Users, eventhough I call the second one with "Company2".
if you go through the documentation here, it says on OnModelCreating method:
Typically, this method is called only once when the first instance of a derived context is created. The model for that context is then cached and is for all further instances of the context in the app domain. This caching can be disabled by setting the ModelCaching property on the given ModelBuidler, but this can seriously degrade performance. More control over caching is provided through use of the DbModelBuilder and DbContext classes directly.
Hope it helps
Related
I am building a Web API and have two models: Task and Feature:
public class Feature
{
[Key]
public long FeatureId { get; set; }
public string Analyst_comment { get; set; }
public virtual ICollection<User_Task> Tasks { get; set; }
public Feature()
{
}
}
public class User_Task
{
[Key]
public long TaskId { get; set; }
public string What { get; set; }
[ForeignKey("FeatureId")]
public long? FeatureId { get; set; }
public User_Task()
{
}
}
I create Tasks first and then create a Feature that combines few of them. Task creation is successful, however while creating a Feature with existing Tasks, my controller throws an error saying the task already exists:
My FeatureController has following method:
//Create
[HttpPost]
public IActionResult Create([FromBody] Feature item)
{
if (item == null)
{
return BadRequest();
}
** It basically expects that I am creating a Feature with brand new tasks, so I guess I will need some logic here to tell EF Core that incoming tasks with this feature already exist **
_featureRepository.Add(item);
return CreatedAtRoute("GetFeature", new { id = item.FeatureId }, item);
}
How to tell EF core that incoming Feature has Tasks that already exist and it just needs to update the references instead of creating new ones?
My context:
public class WebAPIDataContext : DbContext
{
public WebAPIDataContext(DbContextOptions<WebAPIDataContext> options)
: base(options)
{
}
public DbSet<User_Task> User_Tasks { get; set; }
public DbSet<Feature> Features { get; set; }
}
And repo:
public void Add(Feature item)
{
_context.Features.Add(item);
_context.SaveChanges();
}
When calling Add on a DBSet with a model that was not loaded from EF, it thinks it is untracked and will always assume it is new.
Instead, you need to load the existing record from the dbcontext and map the properties from the data passed into the API to the existing record. Typically that is a manual map from parameter object to domain. Then if you return an object back, you would map that new domain object to a DTO. You can use services like AutoMapper to map the domain to a DTO. When you're done mapping, you only need to call SaveChanges.
Generally speaking, loading the record and mapping the fields is a good thing for the security of your API. You wouldn't want to assume that the passed in data is pristine and honest. When you give the calling code access to all the properties of the entity, you may not be expecting them to change all the fields, and some of those fields could be sensitive.
I'm currently completely unable to call .Include() and intellisense (in vscode) doesn't seem to think it exists.
Now after a long time searching the web I've found this:
Not finding .Include() method in my EF implementing Generic repository
which seems to suggest that .Include exists only in System.Data.Entities, which is only available for EF 5 and 6.
So how do i eager load my list property for an entity in EF core?
heres my context
public class Database : DbContext
{
//Set new datasources like this: public DbSet<class> name { get; set; }
public DbSet<Domain.Resource> Resources { get; set; }
public DbSet<Domain.ResourceType> ResourceTypes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=./something.db");
}
}
Heres the data classes:
public class Resource
{
public int ResourceId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int ResourceTypeId { get; set; }
public ResourceType ResourceType { get; set; }
}
public class ResourceType
{
public int ResourceTypeId { get; set; }
public string Name { get; set; }
public List<Resource> Resources { get; set; }
}
Then I do something like:
public List<ResourceType> GetAll()
{
var router = new Database();
var result = router.ResourceTypes.Include(rt => rt.Resources); //It's here there's absolutely no .Include method
return result.ToList();
}
Does .Include not exist in EF Core?
It's a direct consequence of a missing reference in the file where I'm making a call to the method (though i'm not quite sure i understand how...)
Anyways, adding:
using Microsoft.EntityFrameworkCore;
like Tseng and Smit suggested, did the trick. (in the file in which i define the function)
Though why that works i have no idea. I thought .include would automatically be available through the DbSet.
Thanks though! :)
Small, late EDIT: as Christian Johansen pointed out in his comment, the reason it needs the import to see the method signature, is that it is an extension method, which is a topic I strongly encourage any up-and-coming C# developer to learn about as it is immensely useful.
If you end up here, a user of EF 6 or below and happen to miss that OP actually mentioned this like I did, you want to add
using System.Data.Entity;
to your class.
Here is a previous answer that is tracking this issue in EF7. It appears it is now 'included'.
I keep getting error when I try to access a model from an edit or details action.
The model backing the 'InjuriesContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database (http://go.microsoft.com/fwlink/?LinkId=238269).
First I tried adding a migration even though I was sure I hadn't changed anything. Still recieved the same error after an update-database.
Then I removed all the migrations and the database and started a clean database with an inital migration and update. Same error. Nothing was changed.
Model is:
public class InjuriesContext : DbContext
{
public InjuriesContext()
: base("DBCon")
{
}
public DbSet<Patient> Patients { get; set; }
public DbSet<Injury> Injuries { get; set; }
}
public class Injury
{
public int Id { get; set; }
public string Type { get; set; }
public int PatientId { get; set; }
}
Here is controller --
public ActionResult Edit(int id = 0)
{
Injury injury = db.Injuries.Find(id);
if (injury == null)
{
return HttpNotFound();
}
return View(injury);
}
It errors on the injuries.find. I do not have any injuries entered so I expect it to return a 404 like my other controllers but it doesn't like something about this. The only difference between this and my other models is the y to ies for plural. Does Entity Framework not handle this?
There should not be any plural restriction, as you defined everything clearly in your classes anyway.
Have you created the Injuries table?
I belive the table Injury will get created automatically. the variable injury might be a bit close, but I have to test this myself.
Rather try:
public class Injury
{
[Key]
public int Id { get; set; }
[Required]
public string Type { get; set; }
[Required]
public int PatientId { get; set; }
}
private InjuriesContext db = new InjuriesContext();
Injury objInjury = db.Injuries.Find(id);
if (objInjury == null)
{
return HttpNotFound();
}
return View(objInjury);
Hope this helps
It turns out my issue was with multiple contexts. I thought you had to create a separate context for each model class. Apparently Entity Framework needs one context. I went through and created a class for my context and put all my DBsets in that class.
public class ProjContexts : DbContext
{
public ProjContexts()
: base("ProjDBCon")
{
}
public DbSet<Patient> Patients { get; set; }
public DbSet<PreHosp> PreHosps { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Injury> Injuries { get; set; }
}
}
Then I removed all the migrations as per this post and enabled the migrations again did an add migration and update then I got the expected result.
Bottom Line--- Don't have multiple context classes in your project. Not sure if this is possible but after changing the above everything is working as expected. Not sure why it was working when I had two separate contexts and added the third? Maybe because they had foreign keys with one another?
I'm currently working on and ASP.NET MVC application in which I have a User entity like follows:
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Role { get; protected set; }
public virtual Location Location { get; protected set; }
}
Where location is just as straightforward:
public class Location
{
public virtual string Id { get; protected set; }
public virtual string Building { get; protected set; }
public virtual string City { get; protected set; }
public virtual string Region { get; protected set; }
}
My complication arises because I want to populate the User from Active Directory and not the database. Additionally, several classes persisted to the database reference a user as a property. I've got an ADUserRepository for retrieval, but I don't know how to integrate these Users into my object graph when the rest is managed by NHibernate.
Is there a way for NHibernate to persist just an id for a User without it being a foreign key to a Users table? Can I map it as a component to accomplish this? I've also looked at implementing IUserType to make the translation. That way it would map to a simple field and ADUserRepository could be put in the chain to resolve the stored Id. Or am I trying to hack something that's not really feasible? This is my first time around with NHibernate so I appreciate any insight or solutions you can give. Thanks.
Update
It appears my best solution on this will be to map the User with an IUserType and inject (preferably with StructureMap) a service for populating the object before its returned. Framed in that light there are a couple of questions here that deal with the topic mostly suggesting the need for a custom ByteCodeProvider. Will I still need to do this in order for IUserType to take a parameterized constructor or do the comments here: NHibernate.ByteCode.LinFu.dll For NHibernate 3.2 make a difference?
using a Usertype to convert user to id and back
public class SomeClass
{
public virtual string Id { get; protected set; }
public virtual User User { get; protected set; }
}
// in FluentMapping (you have to translate if you want to use mapping by code)
public SomeClassMap()
{
Map(x => x.User).Column("user_id").CustomType<UserType>();
}
public class UserType : IUserType
{
void NullSafeSet(...)
{
NHibernateUtil.Int32.NullSafeSet(cmd, ((User)value).Id, index);
}
void NullSafeGet(...)
{
int id = (int)NHibernateUtil.Int32.NullSafeGet(cmd, ((User)value).Id, index);
var userrepository = GetItFromSomeWhere();
return userrepository.FindById(id);
}
}
I would like to have a reference between two entities stored in the RavenDB document database. Since this is not a relational db I know that I am supposed to use the Denormalized Reference technique described on RavenDBs documentation. Whilst at first this seems fine, once I start to create a real-world domain ‘hierarchy’ including bidirectional references the effort of keeping all those references up to date feels disproportionate. I feel I may be going wrong somewhere.
Can you explain the best / simplest way to model a reasonably complex domain hierarchy using RavenDB?
Thanks
I am not sure whether this will go far enough to answer your question but here is how I go about creating a Denormalized Reference in RavenDB (this is taken from real code with non-essentials removed for clarity)
Domain
public class User : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
public string Id { get; set; }
public Guid FormsAuthenticationGuid { get; set; }
}
public class Assessment
{
public string Id { get; set; }
public UserReference User { get; set; }
public AssessmentState State { get; set; }
}
You can see that I have a Assessment class that references a User. This user reference are managed using the UserReference class below.
Denormalized Reference
public class UserReference
{
public string Id { get; set; }
public string UserName { get; set; }
public static implicit operator UserReference(User user)
{
return new UserReference
{
Id = user.Id,
UserName = user.UserName
};
}
}
Note how the reference class also carries the UserName. This value will not change very often but it may change so we need a way to update the UserName property in the UserReference property held in the Assessment class. To make the change we must first find the correct Assessment instances from RavenDB and for that we need an index.
Raven Index
public class Assessment_ByUserId : AbstractIndexCreationTask<Assessment>
{
public Assessment_ByUserId()
{
Map = assessments => from assessment in assessments
select new
{
User_Id = assessment.User.Id
};
}
}
This index needs to be invoked whenever a User's UserName value is updated. I have a UserService class that helps me co-ordinate all my User related functions, so that is where I put this code.
I reuse this code for other references so it has been abstracted out a little. This may help you create the more complex hierarchies (or perhaps 'domain graph' is a better description) you want.
UserService
public static void SetUserName(IDocumentSession db, string userId, string userName)
{
var user = db.Load<User>(userId);
user.UserName = userName;
db.Save(user);
UpdateDenormalizedReferences(db, user, userName);
}
private static void UpdateDenormalizedReferences(IDocumentSession db, User user, string userName)
{
db.Advanced.DatabaseCommands.UpdateByIndex(
RavenIndexes.IndexAssessmentByUserId,
GetQuery(user.Id),
GetUserNamePatch(userName),
allowStale: true);
}
private static IndexQuery GetQuery(string propertyValue, string propertyName = "User_Id")
{
return new IndexQuery {Query = string.Format("{0}:{1}", propertyName, propertyValue)};
}
private static PatchRequest[] GetUserNamePatch(string referenceValue, string referenceName = "User")
{
return new[]
{
new PatchRequest
{
Type = PatchCommandType.Modify,
Name = referenceName,
Nested = new[]
{
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = referenceValue
}
}
}
};
}
That is it. And you know, now that I lay it all out I can see what you mean. It is a lot of work just to update a reference. Perhaps the Service code can be made more DRY and reused for different relationship types, but I don't see how to get away from writing lots of indexes, one per referenced type.