Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RelationshipDiscoveryConvention rediscovers entity while it is being ignored #3845

Closed
ryanwinter opened this issue Nov 23, 2015 · 3 comments
Closed
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@ryanwinter
Copy link

I was playing around with async loading, and added the follow method to one of my Models:

[NotMapped]
public NotifyTaskCompletion<Geometry> StartImage
{
    get; set;
}

When I tried to add a migration (Add-Migration), the command never completed, and just kept chewing through CPU.

NotifyTaskCompletion is as follows:

using System;
using System.ComponentModel;
using System.Threading.Tasks;

namespace GymBuilder
{
    public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
    {
        public NotifyTaskCompletion(Task<TResult> task)
        {
            Task = task;
            if (!task.IsCompleted)
            {
                TaskCompletion = WatchTaskAsync(task);
            }
        }

        private async Task WatchTaskAsync(Task task)
        {
            try
            {
                await task;
            }
            catch {}

            var propertyChanged = PropertyChanged;
            if (propertyChanged == null) return;

            propertyChanged(this, new PropertyChangedEventArgs("Status"));
            propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
            propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));

            if (task.IsCanceled)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
            }
            else if (task.IsFaulted)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
                propertyChanged(this, new PropertyChangedEventArgs("Exception"));
                propertyChanged(this, new   PropertyChangedEventArgs("InnerException"));
                propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
            }
            else
            {
                propertyChanged(this, new   PropertyChangedEventArgs("IsSuccessfullyCompleted"));
                propertyChanged(this, new PropertyChangedEventArgs("Result"));
            }
        }

        public Task<TResult> Task { get; private set; }
        public Task TaskCompletion { get; private set; }
        public TResult Result { get { return (Task.Status ==    TaskStatus.RanToCompletion) ? Task.Result : default(TResult); } }
        public TaskStatus Status { get { return Task.Status; } }
        public bool IsCompleted { get { return Task.IsCompleted; } }
        public bool IsNotCompleted { get { return !Task.IsCompleted; } }
        public bool IsSuccessfullyCompleted { get { return Task.Status ==   TaskStatus.RanToCompletion; } }
        public bool IsCanceled { get { return Task.IsCanceled; } }
        public bool IsFaulted { get { return Task.IsFaulted; } }
        public AggregateException Exception { get { return Task.Exception; } }
        public Exception InnerException { get { return (Exception == null) ? null :     Exception.InnerException; } }
        public string ErrorMessage { get { return (InnerException == null) ? null :     InnerException.Message; } }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
@rowanmiller
Copy link
Contributor

Hey,

I haven't been able to reproduce this. Below is the model I have been using and Add-Migration Initial successfully adds a migration on both RC1 and the latest nightly builds. Could you share a full code listing we can use to reproduce this issue.

~Rowan

using Microsoft.Data.Entity;
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Spatial;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new BloggingContext())
            {
                db.Blogs.Add(new Blog { Id = 22, Url = "blogs.msdn.com/dotnet" });
                db.SaveChanges();
            }
        }
    }

    public class Blog
    {
        public int Id { get; set; }
        public string Url { get; set; }

        [NotMapped]
        public NotifyTaskCompletion<Geometry> StartImage
        {
            get; set;
        }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Repro;Trusted_Connection=True;");
        }
    }

    public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
    {
        public NotifyTaskCompletion(Task<TResult> task)
        {
            Task = task;
            if (!task.IsCompleted)
            {
                TaskCompletion = WatchTaskAsync(task);
            }
        }

        private async Task WatchTaskAsync(Task task)
        {
            try
            {
                await task;
            }
            catch { }

            var propertyChanged = PropertyChanged;
            if (propertyChanged == null) return;

            propertyChanged(this, new PropertyChangedEventArgs("Status"));
            propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
            propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));

            if (task.IsCanceled)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
            }
            else if (task.IsFaulted)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
                propertyChanged(this, new PropertyChangedEventArgs("Exception"));
                propertyChanged(this, new PropertyChangedEventArgs("InnerException"));
                propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
            }
            else
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
                propertyChanged(this, new PropertyChangedEventArgs("Result"));
            }
        }

        public Task<TResult> Task { get; private set; }
        public Task TaskCompletion { get; private set; }
        public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); } }
        public TaskStatus Status { get { return Task.Status; } }
        public bool IsCompleted { get { return Task.IsCompleted; } }
        public bool IsNotCompleted { get { return !Task.IsCompleted; } }
        public bool IsSuccessfullyCompleted { get { return Task.Status == TaskStatus.RanToCompletion; } }
        public bool IsCanceled { get { return Task.IsCanceled; } }
        public bool IsFaulted { get { return Task.IsFaulted; } }
        public AggregateException Exception { get { return Task.Exception; } }
        public Exception InnerException { get { return (Exception == null) ? null : Exception.InnerException; } }
        public string ErrorMessage { get { return (InnerException == null) ? null : InnerException.Message; } }

        public event PropertyChangedEventHandler PropertyChanged;
    }

}

@ryanwinter
Copy link
Author

Hi Rowan,

Thanks for working on this problem!

Your example worked for me too. I managed to get it to freeze up by adding a second [NotMapped] as below.

Ryan

using Microsoft.Data.Entity;
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Xaml.Media;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new BloggingContext())
            {
                db.Blogs.Add(new Blog { Id = 22, Url = "blogs.msdn.com/dotnet" });
                db.SaveChanges();
            }
        }
    }

    public class Blog
    {
        public int Id { get; set; }
        public string Url { get; set; }

        [NotMapped]
        public bool IsWeight
        {
            get { return Url == ""; }
        }

        [NotMapped]
        public NotifyTaskCompletion<Geometry> StartImage
        {
            get; set;
        }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string databaseFilePath = "GymBuilder.db";
            try
            {
                databaseFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, databaseFilePath);
            }
            catch (InvalidOperationException)
            { }

            optionsBuilder.UseSqlite($"Data source={databaseFilePath}");
        }
    }

    public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
    {
        public NotifyTaskCompletion(Task<TResult> task)
        {
            Task = task;
            if (!task.IsCompleted)
            {
                TaskCompletion = WatchTaskAsync(task);
            }
        }

        private async Task WatchTaskAsync(Task task)
        {
            try
            {
                await task;
            }
            catch { }

            var propertyChanged = PropertyChanged;
            if (propertyChanged == null) return;

            propertyChanged(this, new PropertyChangedEventArgs("Status"));
            propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
            propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));

            if (task.IsCanceled)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
            }
            else if (task.IsFaulted)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
                propertyChanged(this, new PropertyChangedEventArgs("Exception"));
                propertyChanged(this, new PropertyChangedEventArgs("InnerException"));
                propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
            }
            else
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
                propertyChanged(this, new PropertyChangedEventArgs("Result"));
            }
        }

        public Task<TResult> Task { get; private set; }
        public Task TaskCompletion { get; private set; }
        public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ?     Task.Result : default(TResult); } }
        public TaskStatus Status { get { return Task.Status; } }
        public bool IsCompleted { get { return Task.IsCompleted; } }
        public bool IsNotCompleted { get { return !Task.IsCompleted; } }
        public bool IsSuccessfullyCompleted { get { return Task.Status ==     TaskStatus.RanToCompletion; } }
        public bool IsCanceled { get { return Task.IsCanceled; } }
        public bool IsFaulted { get { return Task.IsFaulted; } }
        public AggregateException Exception { get { return Task.Exception; } }
        public Exception InnerException { get { return (Exception == null) ? null : Exception.InnerException; } }
        public string ErrorMessage { get { return (InnerException == null) ? null : InnerException.Message; } }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

@rowanmiller
Copy link
Contributor

Thanks! I see the hang on RC1. On our latest nightly it does not hang, but you now get the following error during Add-Migration. This reproduces on both SQL Server and SQLite.

PM> Add-Migration First
System.ArgumentNullException: Value cannot be null.
Parameter name: entityTypeBuilder
   at Microsoft.Data.Entity.Utilities.Check.NotNull[T](T value, String parameterName)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.RelationshipDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.RelationshipDiscoveryConvention.ApplyOnRelatedEntityTypes(InternalModelBuilder modelBuilder, EntityType entityType)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.RelationshipDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder, EntityType oldBaseType)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.ConventionDispatcher.OnBaseEntityTypeSet(InternalEntityTypeBuilder entityTypeBuilder, EntityType previousBaseType)
   at Microsoft.Data.Entity.Metadata.Internal.InternalEntityTypeBuilder.HasBaseType(EntityType baseEntityType, ConfigurationSource configurationSource)
   at Microsoft.Data.Entity.Metadata.Internal.InternalModelBuilder.Ignore(EntityType entityType, ConfigurationSource configurationSource)
   at Microsoft.Data.Entity.Metadata.Internal.InternalModelBuilder.RemoveEntityTypesUnreachableByNavigations(ConfigurationSource configurationSource)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.ModelCleanupConvention.Apply(InternalModelBuilder modelBuilder)
   at Microsoft.Data.Entity.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
   at Microsoft.Data.Entity.Metadata.Internal.Model.Validate()
   at Microsoft.Data.Entity.Metadata.Internal.InternalModelBuilder.Validate()
   at Microsoft.Data.Entity.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.Data.Entity.Infrastructure.ModelSource.<>c__DisplayClass8_0.<GetModel>b__0(Type k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Data.Entity.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.Data.Entity.Internal.DbContextServices.CreateModel()
   at Microsoft.Data.Entity.Internal.LazyRef`1.get_Value()
   at Microsoft.Data.Entity.Internal.DbContextServices.get_Model()
   at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_5(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.Data.Entity.Design.Internal.DesignTimeServicesBuilder.<>c__DisplayClass7_0.<ConfigureContextServices>b__8(IServiceProvider _)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.Data.Entity.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.Data.Entity.Design.OperationExecutor.<AddMigrationImpl>d__7.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.<>c__DisplayClass4_0`1.<Execute>b__0()
   at Microsoft.Data.Entity.Design.OperationExecutor.OperationBase.Execute(Action action)
Value cannot be null.
Parameter name: entityTypeBuilder

Here is the code stripped down to the smallest code that produces the issue.

using Microsoft.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Spatial;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new BloggingContext())
            {
                db.Blogs.Add(new Blog { Id = 22, Url = "blogs.msdn.com/dotnet" });
                db.SaveChanges();
            }
        }
    }

    public class Blog
    {
        public int Id { get; set; }
        public string Url { get; set; }

        [NotMapped]
        public bool IsWeight { get; set; }

        [NotMapped]
        public Task<Geometry> StartImage { get; set; }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Data source=GymBuilder.db");
        }
    }
}

@rowanmiller rowanmiller added this to the 7.0.0-rc2 milestone Dec 8, 2015
@smitpatel smitpatel changed the title Add-Migration fails with [NotMapped] NotifyTaskCompletion method RelationshipDiscoveryConvention rediscovers entity while it is being ignored. Dec 10, 2015
@smitpatel smitpatel changed the title RelationshipDiscoveryConvention rediscovers entity while it is being ignored. RelationshipDiscoveryConvention rediscovers entity while it is being ignored Dec 10, 2015
@ajcvickers ajcvickers modified the milestones: 1.0.0-rc2, 1.0.0 Oct 15, 2022
@ajcvickers ajcvickers added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants