I have a simple schema that looks like this:
The idea is that there are Users, Departments and Flows. For each Department, a User can "star" multiple Flows to "bookmark" them (therefore Department/User pair can have many Flows).
Ideally, I'd like to use the following classes:
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
}
public class Department
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IDictionary<User, IList<StarredFlow>> StarredFlows { get; protected set; }
public Department()
{
this.UserStarredFlows = new Dictionary<User, IList<StarredFlow>>();
}
}
public class Flow
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
protected Flow() { }
}
public class StarredFlow
{
public virtual int Id { get; protected set; }
public virtual User User { get; protected set; }
public virtual Department EntryPoint { get; protected set; }
public virtual Flow Flow { get; protected set; }
public virtual string Name { get; protected set; }
protected StarredFlow() { }
public StarredFlow(User user, Department ep, Flow flow, string name)
{
this.User = user;
this.EntryPoint = ep;
this.Flow = flow;
this.Name = name;
}
}
This would allow pretty literal code:
var userStarredFlowsForDepartment = department.StarredFlows[currentUser];
However, NHibernate doesn't support mapping dictionaries of lists. I found a similar question/answer that involves moving the IList<StarredFlow> to a new class and mapping it using a component. Sadly, FluentNHibernate doesn't support doing collection mapping with components (there's no HasMany off of the component mapping).
This seems like a trivial thing to do with an NHibernate, but I'm struggling to find a solution. Is there a better solution to doing this? I'd really like to not have to resolve to just using session.Save(starredFlow) and Query<StarredFlow>() because they move the logic outside of the models.
What you want to do is definitely not supported. I'm not saying it wouldn't be a theoretically valid design, but NHibernate does not support it and it's unlikely that it will do so in the future.
That said, you can always have a method in Department instead of a dictionary, taking an IQueryable:
public IEnumerable<StarredFlow> GetUserStarredFlows(IQueryable<StarredFlow> source,
User user)
{
return source.Where(x => x.User == user)
.ToList(); //you could skip this depending on your usage
}
Or (a bit more complex) you could inject the IQueryable or the session.
Related
I have the following entity
public class Meeting
{
[Key]
public int Id { get; set; }
public Guid SubjectId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
public string Email { get; set; }
private ICollection<MeetingPeriod> _meetingTimes;
public virtual ICollection<MeetingPeriod> MeetingTimes
{
get { return _meetingTimes ?? (_meetingTimes = new Collection<MeetingPeriod>()); }
protected set { _meetingTimes = value; }
}
}
Got a few questions:
When I get a record I would like all the MeetingTimes to be sorted by default. Is this possible? I tried to return _meetingTimes.OrderBy but then I could not add any more meeting times as it was a readonly list.
I have added override Equals() to MeetingPeriod object. However when I try to compare MeetingTimes to an array of MeetingPeriod seems like the Equal() isn't getting called. What is going on here? It does get called correctly elsewhere. Seems like something to do with ICollection?
EDIT: I originally worded this question very poorly, stating the problem was with JSON serialization. The problem actually happens when I'm converting from my base classes to my returned models using my custom mappings. I apologize for the confusion. :(
I'm using .NET Core 1.1.0, EF Core 1.1.0. I'm querying an interest and want to get its category from my DB. EF is querying the DB properly, no problems there. The issue is that the returned category has a collection with one interest, which has one parent category, which has a collection with one interest, etc. When I attempt to convert this from the base class to my return model, I'm getting a stack overflow because it's attempting to convert the infinite loop of objects. The only way I can get around this is to set that collection to null before I serialize the category.
Interest/category is an example, but this is happening with ALL of the entities I query. Some of them get very messy with the loops to set the relevant properties to null, such as posts/comments.
What is the best way to address this? Right now I'm using custom mappings that I wrote to convert between base classes and the returned models, but I'm open to using any other tools that may be helpful. (I know my custom mappings are the reason for the stack overflow, but surely there must be a more graceful way of handling this than setting everything to null before projecting from base class to model.)
Classes:
public class InterestCategory
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Interest> Interests { get; set; }
}
public class Interest
{
public long Id { get; set; }
public string Name { get; set; }
public long InterestCategoryId { get; set; }
public InterestCategory InterestCategory { get; set; }
}
Models:
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public long? InterestCategoryId { get; set; }
}
Mapping functions:
public static InterestCategoryModel ToModel(this InterestCategory category)
{
var m = new InterestCategoryModel
{
Name = category.Name,
Description = category.Description
};
if (category.Interests != null)
m.Interests = category.Interests.Select(i => i.ToModel()).ToList();
return m;
}
public static InterestModel ToModel(this Interest interest)
{
var m = new InterestModel
{
Name = interest.Name,
Description = interest.Description
};
if (interest.InterestCategory != null)
m.InterestCategory = interest.InterestCategory.ToModel();
return m;
}
This is returned by the query. (Sorry, needed to censor some things.)
This is not .NET Core related! JSON.NET is doing the serialization.
To disable it globally, just add this during configuration in Startup
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}));
edit:
Is it an option to remove the circular references form the model and have 2 distinct pair of models, depending on whether you want to show categories or interests?
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Note that each of the models has a nested class for it's child objects, but they have their back references removed, so there would be no infinite reference during deserialization?
I have to produce an output from 3 separate tables(with a couple of fields from each table) into 1 output. I have a class that represents that output. The data is pulled from linq query of EF 6.1.x ObjectContext(Im stuck with using ObjectContext due to the nature of my clients needs....) entities (the 3 classes properly joined in the query) to a list of the new class (List<>). I populate a grid and all is fine. However the user wants to edit the data in the grid and now I need to push those new changes back.
My question is this: Can I map my new class back to the entities field to field? Or am I stuck with iterating through the collection and updating the tables individually? I thought I could map but I haven't run across anything that substantiates this.
Could you not do this using the "Proxy" pattern?
I've done a 2 entity + Wrapper example pseudo example below.
EF would "Save" the SuperWrapper.DeptProxy and the SuperWrapper.EmpProxy.
public partial class DepartmentEFEntity {
public virtual Guid? DepartmentUUID { get; set; }
public virtual string DepartmentName { get; set; }
public virtual ICollection<EmployeeEFEntity> Employees { get; set; }
}
public partial class EmployeeEFEntity
{
public virtual Guid? ParentDepartmentUUID { get; set; }
public virtual Guid? EmployeeUUID { get; set; }
public virtual DepartmentEFEntity ParentDepartment { get; set; }
public virtual string SSN { get; set; }
}
public class SuperWrapper
{
internal DepartmentEFEntity DeptProxy { get; private set; }
internal EmployeeEFEntity EmpProxy { get; private set; }
public SuperWrapper(DepartmentEFEntity dept, EmployeeEFEntity emp)
{
this.DeptProxy = dept;
this.EmpProxy = emp;
}
public string DepartmentName
{
get { return null == this.DeptProxy ? string.Empty : this.DeptProxy.DepartmentName; }
set { if(null!=this.DeptProxy{this.DeptProxy.DepartmentName =value;}}
}
public string EmployeeSSN
{
get { return null == this.EmpProxy ? string.Empty : this.EmpProxy.SSN; }
set { if(null!=this.EmpProxy{this.EmpProxy.SSN =value;}}
}
}
Is there a way to define the following structure in a DataContext/DBML file?
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public EntitySet<IPermission> Permissions { get; set; }
}
public class User : IPermissionHolder
{
public int Id { get; set; }
public string Name { get; set; }
public EntitySet<Permission<User>> Permissions { get; set; }
}
public class Group : IPermissionHolder
{
public int Id { get; set; }
public string Name { get; set; }
public EntitySet<Permission<Group>> Permissions { get; set; }
}
public interface IPermissionHolder
{
int Id { get; set; }
}
public interface IPermission
{
Entity Entity { get; set; }
IPermissionHolder Holder { get; }
}
public class Permission<T> : IPermission where T : class, IPermissionHolder
{
public IPermissionHolder Holder
{
get { return PermissionHolder; }
}
public T PermissionHolder { get; set; }
public Entity Entity { get; set; }
}
If it's not possible, can you seggest another structure that fits my need?
Right now my DB is using two different tables for the GroupPermissions and the UserPermissions.
I don't like to have a common table where i have to add a "type" column... with two different table i have a much more strict control on the DB side.
Thanks for any help
P.S.: i'm still with the Framework 3.5, otherwise i could remove the IPermissionHolder interface and use co-variance
P.S.S.: asked also here, but no answer :(
http://social.msdn.microsoft.com/Forums/en/linqtosql/thread/04a03c68-79c0-4136-907c-f81440e78c45
EDIT:
i'm trying different things and i'm facing two main problems
1) I want to have a IEnumerable, but it will never works because i don't want only to get data, but also to push data and an object can not be covariant and contravariant at the same time.
So first of all i should choose: read or write.
2)Here the most difficult issue: how do i map TWO Association to a single property?
User:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name = "User_Permission", Storage = "permissions", ThisKey = "Id", OtherKey = "UserId")]
public EntitySet<Permission<User>> Permissions{ ... }
Group
[global::System.Data.Linq.Mapping.AssociationAttribute(Name = "Group_Permission", Storage = "permissions", ThisKey = "Id", OtherKey = "GroupId")]
public EntitySet<Permission<Group>> Permissions { ... }
Permission
[global::System.Data.Linq.Mapping.AssociationAttribute(Name = "???", Storage = "holder", ThisKey = "HolderId", OtherKey = "Id", IsForeignKey = true)]
public T PermissionHolder { ... }
Maybe i should call the Asscoiation "Holder_Permission"?!?
I tried with many different approach. I can say that with LINQ-TO-SQL is not possible to have generic mapping.
I will try with the Linq-To-Entity.
I am creating a drivers license object in my project, that employees will all have one of their own linked to their unique clock number. I have a separate table in my database for the driving license but in the future more types of vehicles will need to be added, is there anyway to do this without re-coding?
the columns in my database are the same as the attributes for the class below
public class LicenseDTO
{
public int ClockNo { get; set; }
public bool CBalance { get; set; }
public bool MR16 { get; set; }
public bool OrderPicker { get; set; }
public bool Reach { get; set; }
public bool Pedestrian { get; set; }
public bool Lorry { get; set; }
public bool Sweeper { get; set; }
public bool Washer { get; set; }
}
EDIT
I have tried to create this the best I could but I feel like it's really long winded and can be done a more efficient way. Here's an updated version of my code.
public class LicenseDTO
{
public int ClockNo { get; set; }
public List<Common.VehicleTypeDTO> Vehicles { get; set; }
}
public class VehicleTypeDTO
{
public string VehicleType { get; set; }
public bool Allowed { get; set; }
}
private void btnClockCardIn_Click(object sender, EventArgs e)
{
Common.LicenseDTO License = new Common.LicenseDTO();
List<Common.VehicleTypeDTO> Vehicles = new List<Common.VehicleTypeDTO>();
Common.VehicleTypeDTO CBalance = new Common.VehicleTypeDTO();
Common.VehicleTypeDTO MR16 = new Common.VehicleTypeDTO();
License.Vehicles = Vehicles;
CBalance.VehicleType = "CBalance";
CBalance.Allowed = true;
MR16.VehicleType = "MR16";
MR16.Allowed = false;
License.Vehicles.Add(CBalance);
License.Vehicles.Add(MR16);
foreach (Common.VehicleTypeDTO Vehicle in License.Vehicles)
{
MessageBox.Show(Vehicle.VehicleType + " " + Vehicle.Allowed);
}
}
Why not to create a table with the types of vehicles? In the future you can access to your table and insert more types.
public class VehicleTypeDTO
{
public int Id{ get; set; }
public string Name{ get; set; }
}
public class LicenseDTO
{
public int Id { get; set; }
public List<VehicleTypeDTO> VehicleTypes { get; set; }
}
You should have made an entity LicenseDTO with the attributes ClockNo and CBalance alongside an array of the type Vehicle. which will be an interface. the interface Vehicle can define any common methods the vehicles have. and all future vehicles will have to implement the interface. that way you dont have to change any code. Your current code cannot be "changed" without editing. You could try to extend your LicenseDTO class with another entityclass which implements the above interface. but there isnt much more you can do without editing.
If you want maintainability use interfaces, repository patterns, abstract classes and dependency injection to start with.
Instead of having multiple bit columns in your database to indicate different types of vehicles, have a single VehicleType table. Then you can add as many different vehicle types as you like and use the VehicleTypeID to uniquely identify them. You can then add more and more vehicle types to the table without having to write more code.
VehicleType
VehicleTypeID int
VehicleTypeName varchar(50)
public class LicenseDTO
{
public int ClockNo { get; set; }
public int VehicleTypeID { get; set; }
}
If you want to have multiple types of vehicles against a single ClockNo then use a list of int:
public class LicenseDTO
{
public int ClockNo { get; set; }
public List<int> VehicleTypes { get; set; }
}
Alternately you could have a reference to the VehicleType objects instead of just the ID's.