Why do Code First classes need navigation properties? - c#

I have some domain classes that look something like this, that I want to model with Code First (in EF 4.3).
public class Foo {
// ...
}
public class Bar {
// ...
public Foo Foo { get; set; }
}
public class Baz {
// ...
public Foo Foo { get; set; }
}
In every example I see though, foreign object references are added in the Foo class. Can my Foo class be agnostic of the Bar and Baz class, or do I really need to do something like this?
public class Foo {
// ...
public virtual Bar { get; set; }
public virtual Baz { get; set; }
}
According to this answer, classes do need to have navigation properties. I'm new at Code First, so can anyone explain why this might be the case? Is there a way I can avoid polluting my Foo class like this by using the Fluent API?
It seems weird to me that Foo would need to know about every class that uses it. Is my design simply fundamentally flawed in some way?

Your problem here will be requirement for one-to-one relation. One-to-one relation in EF is mapped through primary keys. You choose principal entity and the dependent entity must have FK on its PK - they must have same PK value to be related. The reason is missing support for unique keys.
Once you accept this limitation you can simply use your model and map it like:
modelBuilder.Entity<Bar>()
.HasRequired(b => b.Foo)
.WithOptional();
modelBuilder.Entity<Baz>()
.HasRequired(b => b.Foo)
.WithOptional();

The other answer is partly correct.
If you want code-forst to bootstrap your database model with relationships between the tables you'll have to define at least in one class a navigation property.
The mapping will of course also work without the relationship, but you won't have the constraints on the database/sql level. Unless you add them with migrations or some other sql-scripts.
Though in your example I am not quite sure what kind of relationship you're trying to define anyhow. Is that supposed to be a one-to-one relationship?
In that case, Foo doesn't need to know about any other class that has a reference to it, as answered in your linked question, only one class needs to have it.

Related

EFCore Base entity model properties with private setters - migration bizarre behavior

It seems private setters inside classes that entity models inherit from cause bizarre issues with EFCore migrations. Consider the following example, where there are multiple classes (Bar and Baz) that inherit from Foo. When running Add-Migration commands multiple times (add/remove the private modifier`), the generated schema is just wrong in multiple ways.
Sometimes the Created property is set to be a dropped column
Sometimes all the the Created properties are set to be removed (and not replaced/renamed with anything).
It's my understanding that EFCore tools don't really treat properties with private setters with any special care. Is this a wrong assumption? If so, why do some private setters work on base classes, but not others? I'm assuming this is a bug in the EF Tools, perhaps related to how it treats naming on properties, considering I have other properties with similar names in the model which might be confusing the tools
public class Context : DbContext
{
public DbSet<Bar> Bars { get; set; }
public DbSet<Baz> Bazs { get; set; }
}
public class Bar : Foo { }
public class Baz : Foo { }
public abstract class Foo
{
protected Foo()
{
Created = DateTimeOffset.UtcNow;
}
public DateTimeOffset? Created { get; private set; }
public DateTimeOffset? Updated { get; set; }
}
Edit -> It seems private setters for DateTimeOffset cause EF Tools to never map them in base classes. However, I have a string property with a private setter with [Required] and [StringLength] attributes, along with builder.Entity<Foo>().HasAlternateKey(x => x.RequiredStringProperty); In that case, EF absolutely maps the property...but only with some of the classes that are inheriting from Foo.
The EF core tools will map private setters of base classes only under certain conditions.
For example, if you have builder.Entity<Bar>().HasAlternateKey(x => x.Created);, then EF tools will map the Created property regardless of whether it has a private or public setter.

Does ignoring a base type in Entity Framework Code First also ignore subclasses?

I'm attempting to simulate a scenario in which I am inheriting from concrete base classes in a 3rd party library, then mapping my own classes using Entity Framework Code First. I would really prefer for my classes to have the same simple name as the base classes. I obviously can't change the class names of the base classes, nor can I change the base class to abstract. As expected, I get the following error:
The type 'EfInheritanceTest.Models.Order' and the type
'EfInheritanceTest.Models.Base.Order' both have the same simple name
of 'Order' and so cannot be used in the same model. All types in a
given model must have unique simple names. Use 'NotMappedAttribute' or
call Ignore in the Code First fluent API to explicitly exclude a
property or type from the model.
As I understand it, in EF6 this is possible so long as only one of the classes is actually mapped. However, if I attempt to ignore the base class using the fluent API, I get the following error instead:
The type 'EfInheritanceTest.Models.Order' was not mapped. Check that
the type has not been explicitly excluded by using the Ignore method
or NotMappedAttribute data annotation. Verify that the type was
defined as a class, is not primitive or generic, and does not inherit
from EntityObject.
... which seems to indicate that by ignoring the base class, I ignore any subclasses as well. Full code below. Any way to work around this and "unignore" the subclass? Or is this a limitation of EF type mapping?
namespace EfInheritanceTest.Models.Base
{
public class Order
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual decimal Amount { get; set; }
}
}
namespace EfInheritanceTest.Models
{
public class Order : Base.Order
{
public virtual DateTime OrderDate { get; set; }
}
}
namespace EfInheritanceTest.Data
{
public class OrdersDbContext : DbContext
{
public OrdersDbContext() : base ("OrdersDbContext") { }
public IDbSet<EfInheritanceTest.Models.Order> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Types<Models.Base.Order>().Configure(c => c.Ignore());
modelBuilder.Types<Models.Order>().Configure(c => c.ToTable("order"));
modelBuilder.Entity<Models.Order>().Map(c =>
{
c.MapInheritedProperties();
c.ToTable("order");
});
}
}
}
It's a bit late, anyway: I was able to solve the issue with very simple configuration:
modelBuilder.Ignore<MyBaseClass>();
So EF don't care about MyBaseClass at all, but works with MyInheritedClass as it is not. Perfect!
The second error was related to:
modelBuilder.Types<Models.Base.Order>().Configure(c => c.Ignore());
as you excluded both classes from EF mapping (it excludes all hierarchy from Models.Base.Order).
There is also an excellent post on EF inheritance mapping.
I don't think that this will work if the child class has the same name as the parent class. I've definitely done this where the derived class has a different name than the parent class, but I doesn't look like this is possible when the names are the same (which I didn't know before). To test it, I took one of my projects where the inheritance was working with EF and I changed the names to fit the naming scheme that you have above and I'm getting the same errors that you list. It looks like this might be a limitation of the EF type mapping. Are you able to change the name of your derived class to be different than the parent?

Update derived entity with One-to-Many children

I have these POCO classes, they're mapped using Fluent API with a TPT (Table per Type) strategy.
public class Base
{
...
}
public class Derived : Base
{
...
public virtual ICollection<Foo> Foos {get; set;} // One-to-Many
}
public class Foo
{
...
public virtual ICollection<Bar> Bars {get; set;} // One-to-Many
}
public class Bar
{
...
}
My repository looks like this.
public class Repo
{
public void Update(Base item)
{
using (var ctx = new DbContext())
{
ctx.Entry(item).State = EntityState.Modified;
ctx.SaveChanges();
}
}
}
Action:
public void DoStuff()
{
Derived item = repo.GetById(1);
item.SomeProp = "xyz"; // new value
item.Foos = GenerateFoosWithBars(); // change children
repo.Update(item);
}
To my surprise Update actually works if I'm only updating the Base or Derived classes. However things turn ugly when I try to update the One-to-Many relations. I found a tutorial on how to Update One-to-Many Entities in EF4. I was really expecting EF to be way smarter then this, I mean I have to do it manually... that's so unlike everything else in EF.
So I started out trying to use Entry cause I wanted it to be generic (being able to update any Base derived class) using Entry.OriginalValues to avoid having to write a query myself. But now shit really hits the fan! Entry.OriginalValues fails with an exception saying that DbSet<Derived> doesn't exists. It's totally right, it doesn't. But it shouldn't as the the Derived is mapped to DbSet<Base> via inheritance.
Clearly I must be doing something wrong or something so different from everyone else as I'm unable to find anything useful on the matter. Haven't EF5 improved on this in anyway?
Any suggestions on how I could approach this problem?
Firstly, I think an Update method is not necessary in the Repository since EF tracks changes and applies then when you call SaveChanges() on the context.
Secondly, the problem might be that you're assigning a new collection to the Foos poperty when yo do: item.Foos = GenerateFoosWithBars(); You shouldn't do that since when EF materializes an object of the Derived type it actually returns a proxy which overrides the virtual Foos collection to use a special kind of lazy loaded collection that it tracks. If you assign a different collection of your own that will not be bound to the context. (I don't think that EF will handle that very well). What you should do is modify the collection items not the collection itself! Hope it helps!

C# fluent nhibernate

Does Fluent NHibernate has a simple method for automapping entities?
Let's say I have some classes like the following one and corresponding classmaps:
public sealed class Hello
{
public String Name { get; set; }
public DateTime Timestamp { get; set; }
}
public class HelloMapping : ClassMap<Hello>
{
public HelloMapping()
{
Not.LazyLoad();
// Some Id here
Map(x => x.Name).Not.Nullable().Length(64);
Map(x => x.Timestamp).Not.Nullable();
}
}
So, does Fluent NHibernate has something like "add every mapped entity like Hello"?
If not, what's the easiest way to let the NHibernate use my mappings provided?
It depends on what you mean by "like"?
Do you mean all entities in the same namespace? Then you can do
public class MyConfiguration : DefaultAutomappingConfiguration {
public override bool ShouldMap(Type type) {
return type.Namespace == typeof(Hello).Namespace;
}
}
Whatever you mean, you can probably set a convention to do what it is you are trying to achieve. See auto mapping in Fluent NHibernate.
Short answer: http://wiki.fluentnhibernate.org/Auto_mapping. You can use objects and basic conventions built into FluentNH to map objects that don't require much custom behavior.
You could also use inheritance to define mappings that have common elements across most or all classes. Say Hello is a base class that defines Id, Name and Timestamp. You can define the mapping for this base class, then either derive from it directly to produce mappings for other objects, or you could define JoinedSubclass mappings for objects that should be stored in a common table structure (usually because they are various flavors of a base class, like CheckingAccount, SavingsAccount and MoneyMarketAccount are all different types of BankAccounts with substantially similar data structures).

How to create a c# class that gives you usage like: ClassName.Property.Subproperty

I want to create a class so I can use it like this:
Website.Urls.User.Edit
Website.Urls.User.Add
Website.Urls.Content.List
How can I do this in c#? (they will return strings)
public class UserClass
{
public string Edit {get;set;}
public string Add {get;set;}
}
public class UrlsClass
{
public UserClass User {get;set;}
}
public class Website
{
public UrlsClass Urls {get;set;}
}
For what do you need that?
If the properties are classes, then you can access the properties of the referenced class. Some would argue that this violates the principle of information-hiding, but I think that somewhat depends on the case. For instance, in an ORM (object relational mapping), a property might be a class mapping a foreign key reference, and that's (possibly) a little better than some other circumstances.
Anway, this would not be a case for implicit properties. Use a declared private field for the property to reference, and check it for null before returning it. If null, then fill the property and return.
It seems to me that you should rethink your architecture as this deepy nested structure implies clumsy design. For further information, see the Law of Demeter.

Categories

Resources