Exclude recursive properties after 3 level from business rule engine - codeeffects

I'm using codeeffect for my business rule engine. I have a user class with manager property with user type.
public class BasicUser
{
public int ID { get; set; }
public string Name { get; set; }
public BasicUser Manager { get; set; }
}
In the business rule engine currently showing User.Manager.Manager.Manage.Manager.ID But I would like to show only 2 levels like User.Manager.Manager.ID
Is there any attribute I can use?

You can use the ParentAttribute to set display names of your User class. Details can be found here.
UPDATED: You can also use the SourceAttribute.MaxTypeNestingLevel property to control the number of hierarchy levels of your source object and all reference types it declares. Details are here. Use the ExcludeFromEvaluationAttribute class to remove any source member from the editor and, consequently, from evaluation. Its documentation is here
UPDATE 2: Consider the following two classes:
public class Address
{
public Address() { }
public string Street { get; set; }
public Address InnerAddress { get; set; }
}
[Source(MaxTypeNestingLevel = 1)]
public class Patient
{
public Patient() { }
public string Name { get; set; }
public Address InnerAddress { get; set; }
}
If I give the Patient class to the rule editor as its source and set the value of the nested levels to 1 or less I get the following menu options:
(
Name
I.e the editor ignores all reference types in the source because the level is 1.
If I give it level 2 I see the following options in the menu:
(
InnerAddress.Street
Name
The editor shows the InnerAddress instance (level one) with its Street property (level 2).
And if I set the level to 4 I get these options:
(
InnerAddress.InnerAddress.InnerAddress.Street
InnerAddress.InnerAddress.Street
InnerAddress.Street
Name
Obviously, options are shown up to the level 4 now. All this options are tested on one of Code Effects demo projects.

Related

How to Ignore Properties at the Class Level for .Net Core Web API Bind Model

Is there a way to specify included or excluded properties for a complex type in a request model like JsonInclude? I would think having to write a POCO for each request and having to add validation attributes for each property on each of those POCOs would be a nightmare.
public class RegisterUserRequest
{
//Ignore all other properties including their requirement attributes
[JsonInclude("FirstName,LastName,Username,Password")]
public User user { get; set; }
[Required]
public string PasswordConfirmation { get; set; }
[Required]
public string Email { get; set; }
}
You can use DefaultContractResolver
The given two links can help you.
https://www.niceonecode.com/question/20711/newtonsoft-json-ignore-property-dynamically-during-serialization
https://www.newtonsoft.com/json/help/html/conditionalproperties.htm
Also, you can cover basic ignore property when ReadOnly, Empty, etc. conditions in the given link.
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-ignore-properties?pivots=dotnet-6-0

EF - how to prevent eager loading to load all nested entities

I've manay-to-many relationship between two entities: Categories <--> Items
public class CategoryMaster
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<SubCategoryMaster> SubCategories { get; set; }
public List<ItemMaster> Items { get; set; }
}
public class ItemMaster
{
public long Id { get; set; }
public string Name { get; set; }
public List<CategoryMaster> Categories { get; set; }
}
Whenever I try to explicit load related items to all/certain categories, it gives me
all related items
related categories to those items
related items to those categories and so on...nested/circular references
db.CategoryMaster
.Include(x=>x.Items)
.Include(x=>x.SubCategories.Select(y=>y.Items))
.ToList();
Hence results in below error while serializing it to JSON on *.cshtml with Json.Encode();
A circular reference was detected while serializing an object of type 'GoGreen.Data.Entities.SubCategoryMaster'.
Since I've disabled the lazy loading at property level, I'm not expecting it to load all nested entities(circular references) at any point of time. Is there a way to load all related level one records i.e. Categories and related items.
Related question - But Iodon't want to go with any of the two ways suggested.
NOTE : I'm more interested in knowing why EF behaves like this. It seems a bug to me.
First approach: you can add attribute above properties you don't want to exclude it from being serialized using [ScriptIgnore], you can create partial class and add your customization if your entities are auto generated
Second approach: Create a Model with only properties you need in your view and select only this model and set your properties
EFcontext.Tabel.include(x=>x...).Select(x=>new MyModel { ... });
One workaround, and please don't kill me :-) After object loading and before serializing, just set the loaded objects which are causing the circular reference to null. I tried it and worked like a charm.
use meta data redirection. figured I would help anyone who stumbled here.
[MetadataType(typeof(CategoryMasterMetadata))]
public partial class CategoryMaster
{
}
public class CategoryMasterMetadata
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public virtual List<SubCategoryMaster> SubCategories { get; set; }
public List<ItemMaster> Items { get; set; }
}

Deleting in EF Code first causes navigational properties to be set to null and empty

I noticed something interesting when I was performing a delete using EF code first. I use the following domain model:
public class User
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Playlist> Playlists { get; set; }
}
public class Playlist
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Track> Tracks { get; set; }
}
public class Track
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual Playlist Playlist { get; set; }
}
The model is configured using:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Playlists).WithRequired(x => x.User).Map(x => x.MapKey("UserId"));
modelBuilder.Entity<Playlist>().HasMany(x => x.Tracks).WithRequired(x => x.Playlist).Map(x => x.MapKey("PlaylistId"));
}
I use a generic repository:
public virtual void Delete(T entity)
{
Database.Set<T>().Remove(entity);
}
I also have a dto that looks like:
public class PlaylistDTO
{
public PlaylistDTO(Playlist playlist)
{
Id = playlist.Id;
Title = playlist.Title;
User = playlist.User.Name;
}
}
In one of my services I am trying to do the following:
public PlaylistDTO Delete(long id)
{
Playlist playlist = playlistRepository.GetById(id);
playlistRepository.Delete(playlist);
unitOfWork.Commit();
return PlaylistDTO(playlist);
}
This code fails. When I stepped through the debugger I noticed something interesting. The moment I call playlistRepository.Delete the navigational properties (User and Tracks) get set to null and empty respectively. Playlist however stays in memory. So when I pass in the playlist to the DTO the code will fail when it is trying to access playlist.User.Name. I wanted to pass this data to the client to display a verification.
Is this behavior correct? Is this by design?
This is how EF works. The problem is that your Playlist forms entity graph with other relations and EF uses very simple rule for tracking entity graphs: All entities in the graph must be tracked - there cannot be reference to entity which is not tracked. I don't give you reference to description of this rule, it is just my observation but I didn't find any single exception to this rule.
Edit: Updated version - I just checked internal implementation and relations are indeed nulled during calling Delete
So what happened in your code.
You marked your Playlist as deleted
EF passes delete operation to the state manager which does the fixup - it will null all relations
You saved changes to the database
Because there are no cascade deletes from Playlist all related objects remain undeleted
Once you saved changes EF internally accepted them and set change tracker to current state
Because the current state of Playlist is non existing (deleted in the database) it was detached from the context
Detaching has broken entity graph and EF fixed it by modifying navigation properties on both ends
The code responsible for nulling from System.Data.Objects.EntityEntry.Delete(doFixup) (doFixup is true) - the class is internal:
if (doFixup && (base.State != EntityState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
In your scenario this should have simple workaround - create DTO before you delete entity.

EF - Saving a new entity with linked existing entities creating duplicates

public class Car {
public string SomeProperty { get; set; }
public Manufacturer Manufacturer { get; set; }
public IList<Color> Colors { get; set; }
}
public class Manufacturer {
public int Id { get; set; }
public string Name { get; set; }
}
public class Color {
public int Id { get; set; }
public string Name { get; set; }
}
I already have tables full of Colors and Manufacturers. When I create a new car I want to be able to assign it a Color and Manufacturer bound from .net MVC.
When I save my new car with
context.Cars.Add(car);
A new Car is created (great) but a new Color and Manufacturer are also created even though these objects already had an Id and Name set that matched the content in the database.
The two solutions I see are to either write a custom save method for car, and tell the context that the Manufacturer and Color are Unchanged.
context.Cars.Add(car);
context.Entry(car.Manufacturer).State = EntityState.Unchanged;
foreach (Color color in car.Colors)
context.Entry(car.Color).State = EntityState.Unchanged;
Alternatively, to load the Manufacturer and Color from EF and then link them to the Car, instead of using the MVC bound objects.
car.Manufacturer = carRepository.GetManufacturer(car.Manufacturer.Id);
car.Colors = carRepository.GetColorsById(car.Colors);
I am not thrilled by either solution as this example is very trivial, but my real cases are far more complicated. I don't really want to have to fiddle around with EF in detail for each object I save. I have lots of complex object graphs to save and this seems very error prone.
Is there a way of making EF behave more like NHibernate, whereby you can give it something with an ID already assigned and it will assume without your intervention that it already exists?
Edit - question clarified to show collection of existing entities as well as many-to-one relationships.
Unfortunately, EF does not have anything like session.Load in NHibernate that allows you to get a proxy from an id.
The usual way to deal with this in EF is create a separate FK field containing the scalar value that corresponds to the reference. For example:
public virtual Manufacturer Manufacturer { get; set; }
public int ManufacturerId { get; set; }
Then you only have to set ManufacturerId and it will be saved correctly.
(So much for "POCO" and "code first". Pffffff)
You can define scalar properties in your entities and bind the values to them instead. Eg add
ManufacturerId and ColorId
public class Car {
public string SomeProperty { get; set; }
public int? ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public int? ColorId { get; set; }
public virtual Color Color { get; set; }
}
Then set those scalar properties when you assign (eg through a DropDownList)
This way you can avoid loading many related entities to populate the entity.

Applying Domain Model on top of Linq2Sql entities

I am trying to practice the model first approach and I am putting together a domain model. My requirement is pretty simple: UserSession can have multiple ShoppingCartItems.
I should start off by saying that I am going to apply the domain model interfaces to Linq2Sql generated entities (using partial classes). My requirement translates into three database tables (UserSession, Product, ShoppingCartItem where ProductId and UserSessionId are foreign keys in the ShoppingCartItem table). Linq2Sql generates these entities for me. I know I shouldn't even be dealing with the database at this point but I think it is important to mention.
The aggregate root is UserSession as a ShoppingCartItem can not exist without a UserSession but I am unclear on the rest. What about Product? It is defiently an entity but should it be associated to ShoppingCartItem?
Here are a few suggestion (they might all be incorrect implementations):
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItem> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItem {
public Guid UserSessionId { get; set; }
public int ProductId { get; set; }
}
Another one would be:
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItem> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItem {
public Guid UserSessionId { get; set; }
public IProduct Product { get; set; }
}
A third one is:
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItemColletion> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItemColletion {
public IUserSession UserSession { get; set; }
public IProduct Product { get; set; }
}
public interface IProduct {
public int ProductId { get; set; }
}
I have a feeling my mind is too tightly coupled with database models and tables which is making this hard to grasp. Anyone care to decouple?
Looks like you are on the right track. Half of the whole "doing DDD right" is having the right base classes. Have a look at this great DDD applied to C# resource:
http://dddpds.codeplex.com/
The source code is available and is very readable.
So, with regards to having ID in the model. The ID is a database thing and the usual approach is to keep all persistence out of the Model and restrict the model to the business logic. However, one normally makes an exception for the identifier and buries it in the Model base class like so:
public class ModelBase {
protected readonly object m_Key;
public ModelBase(object key) {
m_Key = key;
}
}
This key is used by your persistence layer to talk to the database and is opaque. It's considered quite OK to downcast the key to the required type, because you know what it is.
Also, the Domain Objects are pretty much on the bottom of your architecture stack (just above the Infrastructure layer). This means that you can make them concrete classes. You will not have multiple implementations of the domain models, so the interfaces are unnecessary, which is what Domain Driven Design is about - Domain first.
public Class UserSession : ModelBase {
public UserSession(Guid Id):base(Id) {}
public Guid Id { get{ return m_Key as Guid;} }
public IList<ShoppingCartItem> ShoppingCartItems{ get; set; }
}
public class ShoppingCartItem : ModelBase {
public ShoppingCartItem ():base(null) {}
public UserSession UserSession { get; set; }
public Product Product { get; set; }
}
Typical shopping cart or customer-order examples prefer making UserSession (or Order) the root of aggregate. Individual items should be children of this session/order. It is up you whether individual items in the cart should have a meaningful id. I would prefer no, since 5 widgets in the cart are indistinguishable from another 5 widgets. Hence, I would model cart items as a collection of value objects.
Common problem with shopping cart items is whether they should include price, or not. if you include price, you will have your cart independent from changes of product price. It is very desirable if you want to store you cart for historical reasons since it is valuable to know how much items in the cart cost according to price when they were bought, not according to current.
Product should form definitively an aggregate by itself. Period.
Now, I don't know if all of this is easily implementable in LINQ to SQL, but you can try.