I have a class:
public class Tool
{
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public Int64 ID { get; set; }
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public string Name { get; set; }
}
My provider has two methods:
public IEnumerable<Tool> GetFirst()
{
using (var db = new Entitites())
{
return db.Tools.FirstOrDefault();
}
}
public void Update(Tool o)
{
using (var db = new Entities())
{
db.Tools.SaveChanges();
}
}
It doesn't work because they are on different contexts, the parameter on Update method is not even being used. I can however get the object from the database and change the fields one by one with the parameter object then save changes.
What should I do?
Update the object and save?
Keep only one context on providers?
Another approach?
I found the attach method from another question, quite similar
using (var db = new Entitites())
{
// Attach the object on this context
db.Attach(tool);
// Change the state of the context to ensure update
db.ObjectStateManager.GetObjectStateEntry(tool).SetModified();
// ClientWins, flawless victory
db.Refresh(RefreshMode.ClientWins, tool);
}
Related
I get an error in my ASP.NET Core Web API project when want to select all content:
System.InvalidOperationException: 'A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext
This is my contentService:
private IContentRepository contentRepository;
private DbContext_CMS dbContext;
public ContentService(IContentRepository contentRepository, DbContext_CMS dbContext )
{
this.contentRepository = contentRepository;
this.dbContext = dbContext;
}
public IEnumerable<ContentInfoDTO> GetAllContent()
{
try
{
IQueryable<Content> contents = contentRepository.GetAll();
IEnumerable<ContentInfoDTO> result = contents.Select(content => new ContentInfoDTO(content)).ToList();
return result;
}
catch (Exception ex)
{
return null;
}
}
This is ContentInfoDTO.cs:
public class ContentInfoDTO
{
public ContentInfoDTO(Content content)
{
try
{
this.Id = content.Id;
this.Title = content.Title;
this.Description = content.Description;
this.BodyHtml = content.BodyHtml;
this.ContentCategories = content.ContentCategories.Select(category => new CategoryInfoDTO(category.FkCmsCategory)).ToList(); //this line error
}
catch (Exception ex)
{
throw;
}
}
public int Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public string? BodyHtml { get; set; }
public ICollection<CategoryInfoDTO>? ContentCategories { get; set; }
}
This is my CategoryInfoDTO:
public class CategoryInfoDTO
{
public CategoryInfoDTO(Category? category)
{
if (category != null)
{
this.Id = category.Id;
this.Title = category.Title;
this.Description = category.Description;
this.FkParentId = category.FkParentId;
}
}
public int Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public int? FkParentId { get; set; }
}
And this is my dbContext configured:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("connectionString");
}
}
I also fixed dependency injection, but it didn't work.
service.AddDbContext<DbContext_CMS>();
In contentService:
IEnumerable<ContentInfoDTO> result = contents.Select(content => new ContentInfoDTO(content)).ToList();
in the Select method you start by getting Content entities from the database. At the moment you start getting your first Content, the DbContext instance provided to you by Dependency Injection is in use.
Next, while still inside the Select method, you take the Content entity, create a new ContentInfoDTO object and pass the entity into the constructor. In the constructor of ContentInfoDTO, specifically at the line you get your error,
this.ContentCategories = content.ContentCategories.Select(category => new CategoryInfoDTO(category.FkCmsCategory)).ToList();
you access the ContentCategories property, which is a navigation property belonging to Content. Since the Content entity you passed into the constructor was provided by EF Core, it is being tracked by EF Core, which means when you tried to perform an operation on ContentCategories property, EF Core tried to perform a lazy loading of all the Category entities related to that Content instance. However, since we're still inside the first Select method and the DbContext instance is still in use there, we tried to access the same DbContext from different places at the same time, hence the error.
You could force the retrieval of the ContentCategories collection earlier using the Include method and see if that solves the problem - see eager loading.
Also, I don't think you need to inject your DbContext in contentService, since you seem to use the repository pattern and retrieve your entities from there, I assume the DbContext is already injected into your repository instance.
Edit : it should be lazy loading instead of explicit loading. Also that's assuming that the lazy loading related package is installed and configuration has been done and that the navigation property is virtual. If your situation doesn't cover any of the requirements, your error may be from something else
Using EF database-first, is it possible to create a duplicate of one of the classes, such that any query made comes back with an additional filter?
As an example: Given a class
public partial class Person
{
public Person()
{
this.Job= new HashSet<Appointments>();
}
public int PersonID { get; set; }
public int JobID { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public virtual ICollection<Appointments> Appointments { get; set; }
}
Is it possible to construct a duplicate of the class in some way that functions like the existing class, but will only return results applied a "where Forename = 'David')
I can't overwrite the existing class (both cases need to be kept, and it'll be overwritten anyway)
My first thought was to simply create a seperate static class with methods that return an IQueryable< Persons>, but to then call that later, the context has been disposed - I don't think you can attach it to a new context?
The best you could do would be to add a function to your DbContext, in a partial class, that returns an IQueryable<Persons> with the filter already applied.
The partial class should have the same name as your actual context class. Any code in the partial class will be merged with the Database-First generated class, as if they were in the same file. It also won't get touched or overwritten by the code-generator if you regenerate the context. You can use this same concept to extend all kinds of code-generated classes (this is exactly the kind of use-case that partial classes were designed for).
public partial class MyDbContext
{
public IQueryable<Persons> FilteredPersons()
{
return this.Persons.Where(p => p.Forename =="David");
}
}
Then you can call it like this:
using (var myContext = new MyDbContext())
{
var query = myContext.FilteredPersons().Where(...some additional filter...);
var results = query.ToList();
}
You could probably also rig something up with an IDBCommandInterceptor, but that would be huge, hacky, ugly, and beyond the scope of a simple answer like this.
I'm attempting to separate my DbContext from a winforms application that I'm currently using to better support a multi-user environment as well as an upcoming website. After doing a bit of research I've going with implementing a data access layer (DAL) for the winforms app/website to connect to and having the end-users work with disconnected entities. My question is regarding the best way I would go about saving updates to my entities when one of the entities in a child collection has been updated.
For instance, if I have the following structure (simplified)
public class Company
{
public int CompanyID { get; set; }
public string CompanyName { get; set; }
public ICollection<Employee> Employees { get; set; } // Non-virtual as we aren't lazy-loading
}
public class Employee
{
public int CompanyID { get; set; }
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Claim> Claims { get; set; }
}
public class Claim
{
public DateTime ClaimDate { get; set; }
public ICollection Documentation { get; set; }
}
public class Document
{
public byte[] DocumentImage { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
}
Inside the winforms application, I have multiple Binding Source's set-up to display the employee's information
For Example:
employeeBinding.DataSource = typeof(Employee); // Eventually set to an IEnumerable<Employee>
claimBinding.DataSource = employeeBinding;
claimBinding.DataMember = "Claims";
documentationBinding.DataSource = claimBinding;
documentationBinding.DataMember = "Documentation";
However, by setting things up like this I'm unable to make calls on the "CurrentChanged" event of each binding source to save each entity since it has changed (unless I have references stored to the previous entity inside the form). So what I have thought to do was something similar to below in the DAL and iterate through each of the child collections.
public void UpdateEmployee(Employee employee)
{
using (myContext context = new myContext())
{
context.Employees.Attach(employee);
context.Entry<Employee>(employee).State = EntityState.Modified;
foreach(var claim in employee.Claims)
{
context.Entry<Claim>(claim).State = EntityState.Modified;
foreach(var doc in claim.Documentation)
{
context.Entry<Document>(doc).State = EntityState.Modified;
}
}
context.SaveChanges();
}
}
However, I feel that this route can get ugly quick with some more complex entities and relationships. Could someone help point me to the best route to handle this or should I have references to the current entities in the code so when the "CurrentChanged" event fires I can just update each individual entity?
Thank you very much.
When you work with Entity Framework you have the ChangeTracker, even if you are using this "Disconected entities" you can have the ChangeTracker tracking the entities, to have this you just need to attach them to the context and before you call the SaveChanges you call .DetectCHanges() You dont really need to have this specific code, you can use generics for this:
public void Update<TEntity>(TEntity entity)
{
using (myContext context = new myContext())
{
context.Set<TEntity>.Attach(entity);
context.ChangeTracker.DetectChanges();
context.SaveChanges();
}
}
the call to the method would be:
Update<Employee>(employees);
Also i think is better for you to use a BindingSouce as the DataSource, and set the DataSource of the BindingSource as a List instead of typeof(Employee)
I could be wrong but I don't believe DetectChanges will be able to determine that there have been changes made to a disconnected entity. When the entity is attached, it will have an EntityState of "Unchanged" so wouldn't the DbContext do nothing with it until you mark it's state as "Modified". Also, as indicated in the following URL, "DetectChanges" is called for a number of methods (including "Attach") anyways and the explicit call would not be needed.
http://msdn.microsoft.com/en-us/data/jj556205.aspx
As for the BindingSource, I was illustrating that that BindingSource will be set to typeof(Employee) as if I was setting up my code in the constructor before the load events where I would actually get my data and set it's datasource to an IEnumerable from the DAL call. If I didn't do this, I would run into issues when attempting to bind to the "DataMember" properties as the other BindingSources wouldn't be able to find the properties indicated.
I don't believe that the code you provided as a sample fixes the issue I'm running into regarding child collections being updated. When testing with LinqPad they'll be updated if the parent entity has changed as well, but not if there have been zero changes to the parent. That's why I was iterating through all child collections and marking them as "Modified".
I have sets of entities all of them are derived from abstract class
public abstract class NamedEntity : INamedEntity
{
#region Public Properties
public string Description { get; set; }
public string Id { get; set; }
public string Name { get; set; }
#endregion
}
When I persist all entities I want to use Name field as a key, so I override DocumentKeyGenerator and provide such implementation:
store.Conventions.DocumentKeyGenerator = entity =>
{
var namedEntity = entity as NamedEntity;
if (namedEntity != null)
{
return string.Format("{0}/{1}", store.Conventions.GetTypeTagName(entity.GetType()), namedEntity.Name);
}
return string.Format("{0}/", store.Conventions.GetTypeTagName(entity.GetType()));
};
It works fine when I persist the list of entities for the first time, but if I want to persist them again I get an exception
PUT attempted on document 'xxxxx' using a non current etag
I just started using RavenDB, so I cannot understand what I am doing wrong?
Just a guess, but it's probably not with your key generation, but how you are storing them.
On first usage you probably have something like:
var myEntity = new MyEntity(...);
session.Store(myEntity);
...
session.SaveChanges();
That part is fine, but on subsequent usage, you should not be doing the same thing. Instead, it should be more like this:
var myEntity = session.Load<MyEntity>("myentities/foobar");
myEntity.Something = 123;
...
session.SaveChanges();
Note there is no call to .Store() when making changes. This is because the entity is "tracked" by the session, and all changes to it are automatically persisted when you call .SaveChanges()
I have been googling and stackoverflowing for the last two hours and couldn't find an answer for my question:
I'm using ASP.NET MVC and NHibernate and all I'm trying to do is to manually map my entities without mapping its base class. I'm using the following convention:
public class Car : EntityBase
{
public virtual User User { get; set; }
public virtual string PlateNumber { get; set; }
public virtual string Make { get; set; }
public virtual string Model { get; set; }
public virtual int Year { get; set; }
public virtual string Color { get; set; }
public virtual string Insurer { get; set; }
public virtual string PolicyHolder { get; set; }
}
Where EntityBase SHOULD NOT be mapped.
My NHibernate helper class looks like this:
namespace Models.Repository
{
public class NHibernateHelper
{
private static string connectionString;
private static ISessionFactory sessionFactory;
private static FluentConfiguration config;
/// <summary>
/// Gets a Session for NHibernate.
/// </summary>
/// <value>The session factory.</value>
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
// Get the connection string
connectionString = ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
// Build the configuration
config = Fluently.Configure().Database(PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString));
// Add the mappings
config.Mappings(AddMappings);
// Build the session factory
sessionFactory = config.BuildSessionFactory();
}
return sessionFactory;
}
}
/// <summary>
/// Add the mappings.
/// </summary>
/// <param name="mapConfig">The map config.</param>
private static void AddMappings(MappingConfiguration mapConfig)
{
// Load the assembly where the entities live
Assembly assembly = Assembly.Load("myProject");
mapConfig.FluentMappings.AddFromAssembly(assembly);
// Ignore base types
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>();
mapConfig.AutoMappings.Add(autoMap);
// Merge the mappings
mapConfig.MergeMappings();
}
/// <summary>
/// Opens a session creating a DB connection using the SessionFactory.
/// </summary>
/// <returns></returns>
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
/// <summary>
/// Closes the NHibernate session.
/// </summary>
public static void CloseSession()
{
SessionFactory.Close();
}
}
}
The error that I'm getting now, is:
System.ArgumentException: The type or
method has 2 generic parameter(s), but
1 generic argument(s) were provided. A
generic argument must be provided for
each generic parameter
This happens when I try to add the mappings. Is there any other way to manually map your entities and tell NHibernate not to map a base class?
IncludeBase<T>
AutoMap.AssemblyOf<Entity>()
.IgnoreBase<Entity>()
.Where(t => t.Namespace == "Entities");
Read more here http://wiki.fluentnhibernate.org/Auto_mapping :)
If you don't want to automap a class, I would recommend using IAutoMappingOverride<T>. I don't about your database, but it might look like:
public class CarOverride : IAutoMappingOverride<Car>
{
public void Override(AutoMapping<Car> mapping){
mapping.Id( x => x.Id, "CarId")
.UnsavedValue(0)
.GeneratedBy.Identity();
mapping.References(x => x.User, "UserId").Not.Nullable();
mapping.Map(x => x.PlateNumber, "PlateNumber");
// other properties
}
}
Assuming you keep these maps centrally located, you could then load them on your autoMap:
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>()
.UseOverridesFromAssemblyOf<CarOverride>();
I know it's an old question but I think that some things are missing here :
When you use IgnoreBase<T> you are telling that you don't want to map inheritance into your database but Fluent Nhibernate will still map your base class as an individual class while you don't tell it not to do that, so if you want to tell Fluent Nhibnernate not to map the class itself you should inherit DefaultAutoMapConfiguration class and override its bool ShouldMap(Type type) and return false if the type is any type that you don't want to map it at all.
When you use AutoMapping generally you don't need any other mapping classes or overrides unless you want to make a change in your mappings and it's not possible doing that using Conventions or you just want to override a small part of one class.(Although you can do the same using Conventions and Inspectors)
You can use IsBaseType convention for your automappings