No defination for Entry? - entity-framework

(.Net 3.1, Entity Framework Core 3.1)
In the documentation
https://learn.microsoft.com/en-us/ef/core/saving/disconnected-entities#saving-single-entities, it implements an InsertOrUpdate.
public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs.Find(blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog); // Error: Entry
}
context.SaveChanges();
}
However, Entry cannot be found for EF-Core 3?

Related

EF Core 3 Has Value Generator

in the modelBUilder for an entity, I am try to have the created and modified dates be set on add and updates by a custom generator. The reason for going this path is because the DbContext that creates the models is being used a base class. This base class is being inherited by SQL Server & SQLite EFCore extensions. Because of this there should be database explicit functionality in the context. The GetDateUTC() and triggers that were originally implemented SQL Server.
modelBuilder.Entity<CommunicationSendRequest>(entity =>
{
...
entity.Property(p => p.CreatedAt).ValueGeneratedOnAdd().HasValueGenerator<CreatedAtTimeGenerator>();
entity.Property(p => p.ModifiedAt).ValueGeneratedOnUpdate().HasValueGenerator<ModifiedAtTimeGenerator>();
});
but what is happening on add and updates both properties always set to new values. Meaning on brand new inserts the modifiedat is set, and on updates the createdat date is set. Which removes the true created at date.
The question is are those have value generators setup correctly? Is there a way to accomplish this using the generators? In the generators I tried to also check the state an return the value only if the state was added or modified. But the state always equaled Detached.
public class CreatedAtTimeGenerator : ValueGenerator<DateTimeOffset>
{
public override DateTimeOffset Next(EntityEntry entry)
{
if (entry == null)
{
throw new ArgumentNullException(nameof(entry));
}
return DateTimeOffset.UtcNow;
}
public override bool GeneratesTemporaryValues { get; }
}
public class ModifiedAtTimeGenerator : ValueGenerator<DateTimeOffset>
{
public override DateTimeOffset Next(EntityEntry entry)
{
if (entry == null)
{
throw new ArgumentNullException(nameof(entry));
}
return DateTimeOffset.UtcNow;
}
public override bool GeneratesTemporaryValues { get; }
}
What I actually did was to go away from the ValueGenerate concept all together and handle it the creation of CreatedAt, ModifiedAt, and added after this post was created DeletedAt dates by overriding the SaveChanges() methods.
public override int SaveChanges()
{
var selectedEntityList = ChangeTracker.Entries()
.Where(x => (x.Entity is IEntityCreatedAt ||
x.Entity is IEntityModifiedAt ||
x.Entity is IEntityIsDeleted) &&
(x.State == EntityState.Added || x.State == EntityState.Modified)).ToList();
selectedEntityList.ForEach(entity =>
{
if (entity.State == EntityState.Added)
{
if (entity.Entity is IEntityCreatedAt)
((IEntityCreatedAt)entity.Entity).CreatedAt = DateTimeOffset.UtcNow;
}
if (entity.State == EntityState.Modified)
{
if (entity.Entity is IEntityModifiedAt)
((IEntityModifiedAt)entity.Entity).ModifiedAt = DateTimeOffset.UtcNow;
if (entity.Entity is IEntityIsDeleted)
if (((IEntityIsDeleted)entity.Entity).IsDeleted)
((IEntityIsDeleted)entity.Entity).DeletedAt = DateTimeOffset.UtcNow;
}
});
return base.SaveChanges();
}

Services.CreateScope() Missing in Asp.net Core 2.1

What replaced Services.CreateScope() in 2.1?
I am trying to follow this tutorial but it seems like stuff has changed quite a bit
https://dotnetthoughts.net/seed-database-in-aspnet-core/
I had the same problem with the ASP.NET Core MVC with Entity Framework Core tutorial, written for .NET Core 2, trying to run it with .NET Core 2.1. It worked fine once I added
using Microsoft.Extensions.DependencyInjection;
to the problem file, in my case Program.cs.
You should replace this line of code:
var host = BuildWebHost(args);
with this
var host = CreateWebHostBuilder(args).Build();
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
try
{
//
}
catch (System.Exception ex)
{
///
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();

Navigation Property Not Loading in the Poco's

i'm using SQLite Provider with Entity Frame work in .Net 4.5
conferControlEntities.Configuration.LazyLoadingEnabled = true;
conferControlEntities.Configuration.ProxyCreationEnabled = true;
conferControlEntities.Configuration.AutoDetectChangesEnabled = true;
conferControlEntities.Configuration.ValidateOnSaveEnabled = true;
and i am also using inheritance in the Entity Framework
so i cannot use the include in the query as i can't include the derived objects navigation property
so i have fillNavigationPropty method in Poco classes which will make a call to the Navigation properties and try to fill them
but whenever i check it will be null
using (var context = GetContext(false,true))
{
var queryable = context.Delegates.Where(i => i.ParentId == id);
foreach (var entity in queryable)
{
entity.FillNavigationProperties();
list.Add(WrapperFactory.Create(entity) as TU);
}
return new ObservableCollection<TU>(list);
}
POCO Example
public class TempPOCO
{
public virtual AnotherPCOCCLassTYpe NavigationProperty1{get;set;}
public void FillNavigationProperties()
{
var tempcall = NavigationProperty1;
}
}

Efficient way of checking if many-to-many relationship exists in EF4.1

I have a many-to-many relationship between two entities - Media and MediaCollection. I want to check if a certain Media already exists in a collection. I can do this as follows:
mediaCollection.Media.Any(m => m.id == mediaId)
However, mediaCollection.Media is an ICollection, so to me this looks like it will have to retrieve every Media in the collection from the database just to make this check. As there could be many media in a collection, this seems very inefficient. I'n thinking that I should use a method of IQueryable, but I can't see how to do this for many-to-many relationships.
How can I check for the existence of the relationship without retrieving the whole collection?
EDIT
I am generating the EF data model from my database, then using the built in VS POCO T4 templates to generate my data context and entity classes. I think the problem is that the generated code does not return EntityCollection for the navigation properties, but instead ObjectSet. ObjectSet implements IQueryable, but does not expose a CreateSourceQuery() method.
Here is a stripped down version of the relevant lines from the context:
public partial class Entities : ObjectContext
{
public const string ConnectionString = "name=Entities";
public const string ContainerName = "Entities";
#region Constructors
public Entities()
: base(ConnectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
public Entities(string connectionString)
: base(connectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
public Entities(EntityConnection connection)
: base(connection, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
#endregion
#region ObjectSet Properties
public ObjectSet<MediaCollection> MediaCollections
{
get { return _mediaCollections ?? (_mediaCollections = CreateObjectSet<MediaCollection>("MediaCollections")); }
}
private ObjectSet<MediaCollection> _mediaCollections;
// snipped many more
#endregion
}
And here is a stripped down version of the class for the MediaCollection entity:
public partial class MediaCollection
{
#region Primitive Properties
// snipped
#endregion
#region Navigation Properties
public virtual ICollection<Medium> Media
{
get
{
if (_media == null)
{
var newCollection = new FixupCollection<Medium>();
newCollection.CollectionChanged += FixupMedia;
_media = newCollection;
}
return _media;
}
set
{
if (!ReferenceEquals(_media, value))
{
var previousValue = _media as FixupCollection<Medium>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupMedia;
}
_media = value;
var newValue = value as FixupCollection<Medium>;
if (newValue != null)
{
newValue.CollectionChanged += FixupMedia;
}
}
}
}
private ICollection<Medium> _media;
private void FixupMedia(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Medium item in e.NewItems)
{
if (!item.MediaCollections.Contains(this))
{
item.MediaCollections.Add(this);
}
}
}
if (e.OldItems != null)
{
foreach (Medium item in e.OldItems)
{
if (item.MediaCollections.Contains(this))
{
item.MediaCollections.Remove(this);
}
}
}
}
// snip
#endregion
}
And finally, here is the FixupCollection that the template also generates:
public class FixupCollection<T> : ObservableCollection<T>
{
protected override void ClearItems()
{
new List<T>(this).ForEach(t => Remove(t));
}
protected override void InsertItem(int index, T item)
{
if (!this.Contains(item))
{
base.InsertItem(index, item);
}
}
}
You can do that but you need a context for that:
bool exists = context.Entry(mediaCollection)
.Collection(m => m.Media)
.Query()
.Any(x => x.Id == mediaId);
Edit:
If you are using ObjectContext API with proxied POCOs instead of DbContext API the former sample will not work. You can try this:
context.ContextOptions.LazyLoadingEnabled = false;
bool exists = ((EntityCollection<Media>)mediaCollection.Media).CreateSourceQuery()
.Any(x => x.Id == mediaId);
context.ContextOptions.LazyLoadingEnabled = true;
So it seems that the built in VS POCO T4 template does not generate anything equivalent to CreateSourceQuery(). No matter! We can code it ourselves. If you add the following code at to the context's .tt file and regenerate:
public ObjectQuery<T> CreateNavigationSourceQuery<T>(object entity, string navigationProperty)
{
var ose = ObjectStateManager.GetObjectStateEntry(entity);
var rm = ObjectStateManager.GetRelationshipManager(entity);
var entityType = (System.Data.Metadata.Edm.EntityType)ose.EntitySet.ElementType;
var navigation = entityType.NavigationProperties[navigationProperty];
var relatedEnd = rm.GetRelatedEnd(navigation.RelationshipType.FullName, navigation.ToEndMember.Name);
return ((dynamic)relatedEnd).CreateSourceQuery();
}
then we can check for the existence of a many-to-many as follows:
var exists = _context.CreateNavigationSourceQuery<Medium>(mediaCollection, "Media")
.Any(m => m.Id == medium.Id);
Props to Rowan's answer on Using CreateSourceQuery in CTP4 Code First for this one.
Try,
mediaCollection.CreateSourceQuery()
.Any(....
CreateSourceQuery will create IQueryable for the association.

Trouble with Include extension on IObjectSet not working

Could anyone help me in assessing why the code below doesn't work. I'm using the common extension method for implementing Include when using IObjectset. In our repositories we were seeing this not returning correctly so I've isolated the code in test app as below. I've also included the interface based Context if this may prove relevant and a screenshot of the relevant model section. This occurs for all Includes on IObjectSet properties not just the DPASelections one I've chosen for this example.
If I update the context to return ObjectSet (still using the POCO entities) rather than IObjectSet it all works fine. When using IObjectSet and the extension method and step through the code I see that the extension method is completing correctly with a call to the ObjectQuery we're casting to but the included entities are never returned on the graph. As said, this works perfectly when I don't interface out the Context and return ObjectSet properties hence calling Include directly on ObjectSet.
I'm not getting any errors on executing the query so this isn't the same as several other questions on SO which refer to compiled queries.
Has anyone else experienced problems with this extension method implementation or can anyone spot what I'm doing wrong here?
Any help very much appreciated.
static void Main(string[] args)
{
using (var context = new AssocEntities())
{
context.ContextOptions.LazyLoadingEnabled = false;
Candidate candidate = context.Candidates
.Include("DPASelections.DPAOption")
.SingleOrDefault(c => c.Number == "N100064");
//Count is 0 when using ext. method and IObjectSet through AssocContext but correct when using Include
//on ObjectSet through AssocContext
Console.WriteLine("DPASelection count = {0}",candidate.DPASelections.Count);
//This is always null when using IObjectSet and ext. method but populated
//when using Include on ObjectSet
var option = candidate.DPASelections.First().DPAOption;
Console.WriteLine("First DPAOption = {0} : {1}",option.Id,option.Text);
}
Console.ReadLine();
}
}
public static class Extensions
{
public static IQueryable<TSource> Include<TSource>(this IQueryable<TSource> source, string path)
{
var objectQuery = source as ObjectQuery<TSource>;
if (objectQuery != null)
{
objectQuery.Include(path);
}
return source;
}
}
//Subset of custom context implementing IObjectSet as returns.
//Works fine when I return ObjectSet rather than IObjectSet and use
//the Include method directly
public partial class AssocEntities : ObjectContext
{
public const string ConnectionString = "name=AssocEntities";
public const string ContainerName = "AssocEntities";
#region Constructors
public AssocEntities()
: base(ConnectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
public AssocEntities(string connectionString)
: base(connectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
public AssocEntities(EntityConnection connection)
: base(connection, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
#endregion
#region IObjectSet Properties
public IObjectSet<Address> Addresses
{
get { return _addresses ?? (_addresses = CreateObjectSet<Address>("Addresses")); }
}
private IObjectSet<Address> _addresses;
public IObjectSet<Answer> Answers
{
get { return _answers ?? (_answers = CreateObjectSet<Answer>("Answers")); }
}
private IObjectSet<Answer> _answers;
public IObjectSet<Candidate> Candidates
{
get { return _candidates ?? (_candidates = CreateObjectSet<Candidate>("Candidates")); }
}
}
And the model...
I needed to replace objectQuery.Include(path); with objectQuery = objectQuery.Include(path);
In .Net framework 4.0 there is a build-in Extentionmethod for Include
just add the System.Data.Entity namespace.
It uses reflection - here is how it works:
private static T CommonInclude<T>(T source, string path)
{
MethodInfo method = source.GetType().GetMethod("Include", DbExtensions.StringIncludeTypes);
if (!(method != (MethodInfo) null) || !typeof (T).IsAssignableFrom(method.ReturnType))
return source;
return (T) method.Invoke((object) source, new object[1]
{
(object) path
});
}