How to configure Domain Classes to use ObservableCollection using EF? - entity-framework

I have started using Entity Framework Code First modeling technique and have seen many examples of implementing one to many (1-N) relationship using DataAnnotation and FluentAPI but all examples are using ICollection while modeling domain classes. I have already used generic ObservableCollection in my domain classes and do not intent to change it.
Currently while specifying the configuration using FluentAPI, i am getting following error:
HasRequired(t => t.App)
.WithMany(t => t.EndPoints) // error here
.HasForeignKey(t => t.App);
Cannot implicitly convert Type 'EndPoints' to 'ICollection'.
Note: EndPoints class is implemented using ObservableCollection.
My question is how to make it work?
Following is my entity definition:
public class ModelBase
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class RuleApp : ModelBase
{
public EndPoints EndPoints { get; set; }
}
public class EndPoint : ModelBase
{
public RuleApp RuleApp { get; set; }
}
public class EndPoints : GenericObservableCollection<EndPoint> { }
public class GenericObservableCollection<T> : ObservableCollection<T>
{
// other common stuff handling
}

This is an Example to do that :
public class ModelBase
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class RuleApp : ModelBase
{
//This for create RelationShip
public virtual ICollection<EndPoint> EndPoints { get; set; }
[NotMapped]
Public EndPoints GenericEndPoints { get; set; }
public void TransferToGenric()
{
GenericEndPoints =new EndPoints(EndPoints)
}
}
public class EndPoint : ModelBase
{
public RuleApp RuleApp { get; set; }
}
public class EndPoints : GenericObservableCollection<EndPoint> { }
public class GenericObservableCollection<T> : ObservableCollection<T>
{
// other common stuff handling
}
If you use GenericObservableCollection as a Property EF Mapped all property in your calss, so I create just a property to use endpoint and after that i transform it to the GenericObserveableCollection.
in the constractor of you EndPoins class you have to featch all data in endpoint to do what you want

Related

ASP.NET Core MVC: how to bind entities to a specific user using Entity Framework

I know my question is kinda broad but I haven't found any good answers online.
I am building an ASP.NET Core MVC web app. I am looking for tips on how to bind a specific user to entities (classes in model). The problem is really about how to bind a certain user to a certain entity in the database.
Any help would be greatly appreciated.
ApplicationUser (or IdentityUser) is like any other entity class.
Creating a 1-n relationship, for example, would look something like:
public class ApplicationUser : IdentityUser
{
public virtual IList<MyEntity> MyEntities { get; set; } // navigation property
}
public class MyEntity
{
public int Id { get; set; }
public string CreatedByUserId { get; set; } // FK
public ApplicationUser CreatedByUser { get; set; } // Navigation property
}
You could also do this the fluent way:
public class ApplicationUser : IdentityUser { }
public class MyEntity
{
public int Id { get; set; }
public string CreatedByUserId { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<MyEntity>(b => {
b.HasOne<ApplicationUser>()
.WithMany()
.HasForeignKey(myEntity => myEntity.CreatedByUser);
});
}
}

Using OData with model inheritance cause missing property error

I got this error in my OData with asp.net core implementation during the runtime :The EDM instance of type '[XXX.Asset Nullable=True]' is missing the property 'externalId'.
The problem appear when I try to access the odata endpoint with the expand query: "/odata/v1/precinct?$expand=assets". It seems happening because I put the "ExternalId" property in my base class, its not happening if I put that property in the "Asset".
Below is my recent codes:
public abstract class Entity
{
public int Id { get; set; }
public string ExternalId { get; set; }
}
public class Precinct : Entity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual IEnumerable<Asset> Assets { get; set; }
}
public class Asset : Entity
{
public string Name { get; set; }
public string Description { get; set; }
}
and here is my model configuration for ODATA
public class AssetModelConfiguration : IModelConfiguration
{
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
{
var org = builder.EntitySet<Asset>("asset").EntityType;
org.HasKey(x => x.ExternalId);
org.Ignore(x => x.Id);
}
}
The strange thing is if I put that ExternalId in "Asset" class, it is working. Id property is the primary key while the "ExternalId" is marked as AlternateKey in the DBModel configuration.
am I missing something in my odata configuration? already tried many things but couldn't find a good answer. Any help would be appreciated!

How to add relationship between tables in two different DbContext

I have two DbContext class and some Entities in them. I know this is not good idea to have more than one DbContext but i have to do a lot of works to change my code! so my question is what is the best scenario for add relationship between two Entities with different DbContext?
For example an one to many relationship between User Entity with IdentityDb Context and Comment Entity with SiteDbContext :
public class User : IdentityUser
{
public DateTime JoinDate { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this,DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
public class IdentityDb : IdentityDbContext<ApplicationUser>
{
public IdentityDb() : base("DefaultConnection", throwIfV1Schema: false)
{
}
public static IdentityDb Create()
{
return new IdentityDb();
}
}
public class Comment
{
public int CommentId { get; set; }
[Required]
[StringLength(900)]
public string CommentText { get; set; }
[DataType(DataType.Date)]
public DateTime CommentDate { get; set; }
}
public class SiteDb: DbContext
{
public SiteDb(): base("DefaultConnection")
{
}
public DbSet<Comment> Comments{ get; set; }
}
Short answer: Entity Framework currently doesn't support creating a query which uses more than one context.
For work around: Refer this.

How to add EntitySet when using Breeze EdmBuilder

FINAL EDIT : I will put the answer here at the top for other people who are searching. The main problem was the namespace mismatch caused by db-first generated EDM attaching '.Models' to the end of the model namespace. This namespace did not matched with the odata namespace so the route was failing. I just edited out all the occurances of '.Models' from the namespace and now it's working.
A newbie trying out Breeze with webApiOdata set up. Sorry if this question is a trivial one.
I have a db generated edmx model with webapi odata controllers. I was having problem with getting the correct metadata to show until I read about the new Breeze EdmBuilder.
That solved the problem of getting the correct metadata to show but now, I can not route to any of the tables. If I try /odata/Customers I get a 406 error.
Before, I was using ODataConventionModelBuilder to set the EntitySets and that worked fine.
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Detail>("Details");
builder.EntitySet<Item>("Items");
builder.EntitySet<Order>("Orders");
builder.EntitySet<Customer>("Customers");
Now, since I am using EdmBuilder, how do I set the EntitySets so that I can route to proper data?
I hope the question makes sense.
* EDIT : I have added the listing of GCSodContext and a snippet from the Customers controller.
namespace GCSbz3.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class GCSodContext : DbContext
{
public GCSodContext()
: base("name=GCSodContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<Detail> Details { get; set; }
public virtual DbSet<Item> Items { get; set; }
public virtual DbSet<Order> Orders { get; set; }
}
}
and Here is the Customers controller
...
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData;
using System.Web.Http.OData.Routing;
using GCSbz3.Models;
namespace GCSbz3.Controllers
{
public class CustomersController : ODataController
{
private GCSodContext db = new GCSodContext();
// GET odata/Customers
[Queryable]
public IQueryable<Customer> GetCustomers()
{
return db.Customers;
}
...
Here is the Customer class.
namespace GCSbz3.Models
{
using System;
using System.Collections.Generic;
public partial class Customer
{
public Customer()
{
this.Orders = new HashSet<Order>();
}
public int CustID { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public string Phone { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
}
Check your ODataController name, it needs to be aligned with EntitySet name in your edmx.
If the set name is CustomerSet, controller needs to be CustomerSetController with a public GetCustomerSet method.
For instance this works;
TestDbContext.cs
public partial class TestDbContext : DbContext
{
public TestDbContext()
: base("name=TestDbContext")
{}
public virtual DbSet<A1> A1Set { get; set; }
}
A1SetController.cs
public class A1SetController : ODataController
{
private TestDbContext db = new TestDbContext();
// GET odata/A1Set
[Queryable]
public IQueryable<A1> GetA1Set()
{
return db.A1Set;
}
}
And routing setup in WebApiConfig.cs
// OData routes
config.Routes.MapODataRoute(
routeName: "odata",
routePrefix: "odata",
model: EdmBuilder.GetEdm<TestDbContext>(),
batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
And last, edmx and actual model (class) namespaces need to be aligned. When you make a request to $metadata, you see the defined namespace for your model. You can change it in your Model Designer, right click to an empty area, click to Properties. In Properties window, you can see Namespace attribute. In my case;
<Schema Namespace="Web">
<EntityType Name="Customer">
And Customer.cs
namespace Web
{
using System;
using System.Collections.Generic;
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
}

How to work with multiple levels of abstractions with Entity Framework and code first?

I have an existing model with multiple levels of inheritence with several of the intermediate classes being abstract. The model looks fine, but when trying to create the database I get the error:
Problem in mapping fragments starting at lines 13, 20:Two entities with different keys are mapped to the same row. Ensure these two mapping fragments do not map two groups of entities with different keys to the same group of rows.
Here's the simplest code I could write to reproduce the error. If I make Pet concrete, the problem goes away. What I can I do to allow multiple abstract classes in my hierarchy?
public abstract class Animal
{
public int Id { get; set; }
}
public abstract class Pet : Animal
{
public string Name { get; set; }
}
public class Fish : Pet
{
public bool IsFreshwater { get; set; }
}
public class Dog : Pet
{
public bool IsNeutered { get; set; }
}
public class Person : Animal
{
public Pet MyPet { get; set; }
}
public class PersonContext : DbContext
{
public DbSet<Person> People { get; set; }
}
[TestFixture]
public class AnimalTests
{
[Test]
public void CanCreateDatabase()
{
Database.SetInitializer(new DropCreateDatabaseAlways<PersonContext>());
using (var context = new PersonContext())
{
Assert.AreEqual(0, context.People.Count());//fails here
}
}
}
Update. Here's a KDiff snap of the differences in the generated .edmx file. The file on the left is my original code that fails and on the right is what is generated when I include a DbSet<Pet> in my DbContext.
You need to add Pets to your context:
// Table-per-class (TPC)
public class PersonContext : DbContext
{
public PersonContext()
{
}
public DbSet<Person> People { get; set; }
public DbSet<Pet> Pets { get; set; }
}
Edit: I just saw your comment where you stated you wanted table by hierarchy inheritance. Your problem, then, is that your DbSet is not typed properly - you want a single DbSet using the base type:
// Table-per-hierarchy (TPH)
public class PersonContext : DbContext
{
public PersonContext()
{
}
public DbSet<Animal> Animals { get; set; }
}