EF domain model

Hi there,

please provide an example of a fully featured domain model with all available properties of your Blazor GANTT component, which can be consumed bei EF (code first).

This one does not work and is incomplete, see attached example

To reproduce the error, please do the following:
  1. start VS2019 and load the solution
  2. PM >  add-migration init
  3. PM > update-database -verbose
  4. start debugging using IIS Express
  5. add two tasks
  6. try to edit the task
Any ideas?

Cheers,
Volker

Attachment: GanttEF_5927fda1.zip

14 Replies

LG Logeswari Gopalakrishnan Syncfusion Team June 1, 2020 02:11 PM UTC

Hi Volker, 
 
 
We analyzed the code snippet shared by you and we note that the  error arises, as the primary key column(Id) was not defined in columns property. By default, id column will be act as a primary key column. Editing is performed based on this primary column. To avoid this issue, we can define the Id column in the GanttColumns property and make its visibility false. Please find the below code example. 
 
<SfGantt ID="GanttExport" TValue="Table" Height="450px" Width="700px" 
…///     
<GanttColumns> 
        <GanttColumn Field=@nameof(Table.Id) Width="100" Visible="false"></GanttColumn> 
         
    </GanttColumns> 
    </SfGantt> 
Please find the below sample link. 
Please let us know if you need further details on this. 
Regards, 
Logeswari G 



VO Volker June 4, 2020 07:25 AM UTC

Hi Logeswari,

thank you, but I need a more detailed solution.

I need the same functionality like in this "Context Menu" example:
https://blazor.syncfusion.com/demos/gantt-chart/context-menu?theme=bootstrap4

BUT:
not hard-coded content as you did, but using EF Core consuming from SQL-Server:


1) We must transfer your domain models to SQL Server entities:





2) We must be able to use all the functions of your context menu, but this time targeting to SQL-Server using EF Core:






3) We must be able to manage resources via checkboxes and send it to SQL-Server using EF Core:





Thanks for your help, it would be urgent ...

Cheers,
Volker


LG Logeswari Gopalakrishnan Syncfusion Team June 5, 2020 03:16 AM UTC

Hi Volker, 
  
Please find the below response.  
  
S.No  
Queries  
Syncfusion Comments  
1. 
We must transfer your domain models to SQL Server entities: 
By using hierarchal data , we can not perform operations on server-side. So we can use self-reference data binding. 
2 
We must be able to use all the functions of your context menu, but this time targeting to SQL-Server using EF Core: 
Currently we are working on this. We will provide the further details on June 8,2020. 
3 
We must be able to manage resources via checkboxes and send it to SQL-Server using EF Core: 
We can manage resources using dialog edit and changes are send to SQL- sever by changing collection of resources to string. Please find the below code snippet. 
  
[Index.razor] 
  
public override object BatchUpdate(DataManager dm, object changedRecords, object addedRecords, object deletedRecords, string keyField, string key, int? dropIndex) 
        { 
            List<GanttData> changed = changedRecords as List<GanttData>; 
            if (changed != null) 
            { 
                for (var i = 0; i < changed.Count(); i++) 
                { 
                    var value = changed[i]; 
                    Table result = db.Table.Where(or => or.Id == value.Id).FirstOrDefault(); 
                    result.Id = value.Id; 
                    result.Name = value.Name; 
                    result.Sdate = value.Sdate; 
                    result.Edate = value.Edate; 
                    result.Progress = value.Progress; 
                    result.ResourceId = value.ResourceId == null ? "" : UpdateResourceID(value.ResourceId); 
                    db.SaveChanges(); 
                } 
            } 
…/// 
} 
  
public string UpdateResourceID(List<GanttResource> ResourceId) 
        { 
            List<string> resArray = new List<string>(); 
            int length = ResourceId.Count; 
            for (var i = 0; i < length; i++) 
            { 
                resArray.Add((ResourceId[i].ResourceId)); 
            } 
            string result = string.Join(",", resArray); 
            return result; 
        } 
  
  
Please find the below sample link. 
  
  
Please let us know, if you need further details on this.  
  
Regards,  
Logeswari G 



VO Volker June 7, 2020 08:56 AM UTC

Hi Logeswari,

please clarify the difference between hierarchical (Child) and self-referential (ParentID) local data binding.
In other words: Do complex GANTT examples exist (just an image of the final diagram, no code needed), that you cannot create with ParentID, but only with Child? Or is it possible to construct every GANNT both with Child and with ParentID without restrictions?

Cheers,
Volker


LG Logeswari Gopalakrishnan Syncfusion Team June 8, 2020 03:29 PM UTC

Hi Volker, 
 
Query 1:  
 
For Hierarchical data binding, Child property is used to map the child records in hierarchical data. For self-referential data , ParentID property is used to map the child records. Please refer the below UG link to get more details about data binding in Gantt Chart. 
 
 
We can not bind both Hierarchical data binding (Child) and self-referential data (ParentID) to construct Gantt Chart.  
 
Query 2: We must be able to use all the functions of your context menu, but this time targeting to SQL-Server using EF Core 
 
We can use all functions of contextMenu and changes can be send to SQL-Server. Please find the below code snippet. 
 
public override object BatchUpdate(DataManager dm, object changedRecords, object addedRecords, object deletedRecords, string keyField, string key, int? dropIndex) 
        { 
 
            List<GanttData> addRecord = addedRecords as List<GanttData>; 
…////// 
            if (addRecord != null) 
            { 
                 
                for (var i = 0; i < addRecord.Count(); i++) 
                { 
                    var added = addRecord[i]; 
                     
 
                    var newRecord = new Table {  
                        Id = added.Id,  
                        Name = added.Name,  
                        Sdate = added.Sdate,  
                        Edate = added.Edate,  
                        Progress = added.Progress, 
                        ParentId = added.ParentId, 
                        ResourceId = added.ResourceId == null ? "" : UpdateResourceID(added.ResourceId) 
                    }; 
 
                    db.Add<Table>(newRecord); 
 
                    db.SaveChanges(); 
 
                } 
            } 
            return (new { addedRecords = addRecord, changedRecords = changed, deletedRecords = deleteRecord }); 
        } 
 
Please find the below sample link. 
 
 
Please let us know, if you need further details on this. 
 
Regards, 
Logeswari G 



VO Volker June 8, 2020 03:44 PM UTC

Hi Logeswari,

ad Query 1:
I really studied your documents believe me, before contacting the support ;-)

Of cause you cannot use both Child and ParentId in the same GANTT, that makes no sense and that was not the question, but:

The question was:
"Please clarify the difference between hierarchical (Child) and self-referential (ParentID) local data binding.
In other words: Do complex GANTT examples exist (just an image of the final diagram, no code needed), that you cannot create with ParentID, but only with Child or the other way round? Or is it possible to construct every GANNT both with Child and alternatively also with ParentID without restrictions?"

ad Query 2:
I'll try to implement your solution this way and will inform you if I was successful. Thanks!

Cheers,
Volker


LG Logeswari Gopalakrishnan Syncfusion Team June 9, 2020 02:57 PM UTC

Hi Volker, 
 
In local data binding, the data source for rendering the Gantt control is retrieved from the same application locally. 
 
 
S.no 
Hierarchical data binding 
self-referential data binding 
1. 
In hierarchical data binding subtasks or child tasks collections are identified by using Child property. 
Gantt can be rendered from self-referential data structures, by mapping the Id and ParentID fields. 
2. 
DataSouce: 
 
public class TaskData 
{ 
…/// 
public List<TaskData> SubTasks { get; set; } 
} 
 
public static List<TaskData> GetTaskCollection() 
{ 
    List<TaskData> Tasks = new List<TaskData>() { 
        new TaskData() { 
            TaskId = 1, 
            TaskName = "Project initiation", 
            StartDate = new DateTime(2019, 04, 02), 
            EndDate = new DateTime(2019, 04, 21), 
            SubTasks = (new List <TaskData> () { 
                new TaskData() { 
                    TaskId = 2, 
                    TaskName = "Identify Site location", 
                    StartDate = new DateTime(2019, 04, 02), 
                    Duration = "0", 
                    Progress = 30, 
                }, 
                …////// 
 
    return Tasks; 
} 
 
DataSource: 
 
public class TaskData 
    { 
..////   
public int? ParentId { get; set; } 
    } 
 
    public static List <TaskData> GetTaskCollection() { 
    List <TaskData> Tasks = new List <TaskData> () { 
 
        new TaskData() { 
            TaskId = 1, 
            TaskName = "Project initiation", 
            StartDate = new DateTime(2019, 04, 02), 
            EndDate = new DateTime(2019, 04, 21) 
        }, 
        new TaskData() { 
            TaskId = 2, 
            TaskName = "Identify Site location", 
            StartDate = new DateTime(2019, 04, 02), 
            Duration = "0", 
            Progress = 30, 
            ParentId = 1 
        }, 
        …/// 
    return Tasks; 
} 
 
 
3. 
TaskFields: 
 
<SfGantt DataSource="@TaskCollection" > 
    <GanttTaskFields Child="SubTasks"> 
    </GanttTaskFields> 
</SfGantt> 
 
TaskFields: 
 
<SfGantt DataSource="@TaskCollection" > 
    <GanttTaskFields ParentID="ParentId"> 
    </GanttTaskFields> 
</SfGantt> 
 
4. 
Sample link for Hierarchical data binding:  
 
Sample link for Flat data:  
 
 
Please let us know, if you need further details on this. 
 
Regards, 
Logeswari  



VO Volker June 16, 2020 02:43 PM UTC

Hi Logeswari,

thank you for your help.

Well, I tested your solution (GanttEF39685192.zip) deeply within the last days.
There is a huge conceptionel problem, which drives me crazy (see point 2 und 3).

To explain the problem I've made a simple test-setup based on  GanttEF39685192.zip, so that you can reproduce the 3 problems, see attachend ZIP.

To reproduce the errors, please do the following:
  1. start VS2019 and load the solution
  2. PM > add-migration init
  3. PM > update-database -verbose
  4. Run the frontend and add a view tasks on page "gantt" so that there is some data in the db.
Everything works fine so far. Alternatively use this sql to create the table:

USE [Gantt-Test]
GO

CREATE TABLE [dbo].[GanttTasks](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NULL,
[Sdate] [datetime] NULL,
[Edate] [datetime] NULL,
[Progress] [nchar](10) NULL,
[ParentId] [int] NULL,
[Duration] [nchar](10) NULL,
[Dependency] [nvarchar](100) NULL,
 CONSTRAINT [PK_GanttTasks] PRIMARY KEY CLUSTERED 
[Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

ON [PRIMARY]
GO

Preface:
As you can see, we have access to the DB both from "Home" and from "Gantt".
Please keep in mind that these two pages use different DbContexts (one is a DI-service via StartUp.cs, the other one is hardcoded in the DataAccessLayer).

Home:


Gantt:




So these are the three  problems with your solution:

(1) Version

I tested the solution with v18.1.0.56, everything worked fine.
When upgrading today to v18.1.0.57 my example (and the example you provided) doesn't work any longer:
So all BatchUpdates within CustomAdaptor are broken (try to modify an existing task, change dates or add a child via context menu).
There seems to be a bug within the latest release v18.1.0.57.

Anyway let's roll back to v18.1.0.56 and go on with the demonstration...



(2) hardcoded DB connection

In the Frontend there is no EntityFramework and db-stuff, just the GUI.
All database stuff is outsourced to the DataAccessLayer.

In DataAccessLayer there is my CustomAdaptor which derives from DataAdaptor.

Because you choose this approach in CustomAdaptor...

GanttManager.cs


...we must init a new DbContext using OnConfiguring:

GanttContext.cs


The problem here is that the connection to SQL-Server is hardcoded in the backend rather than using the connectionstring from the Frontend's appsetting.json.
That's a massive problem to production scenarios.

appsettings.json


We must (!) use the connection string of FrontEnd to configure the backend (DataAccessLayer) and your CustomAdaptor.
Please modify the example to reach this goal!



(3)  Multiple DbContexts

As I demonstrated on the index-page we have conventional access to the database using GanttContext and DI as we normally inject things as a service.

Startup.cs:


Index.razor:


Cool!

But in your CustomAdaptor you add another DBContext...

GanttManager.cs


Not cool!

So instead of using the DbContext of Frontend's Startup.cs service (DI, connectionstring, appsettings.json) we add a second one here.
That is dangerous and can cause concurency exceptions in FrontEnd on editing, when there is a grid, a dialog and a Gantt-diagram (with editing enabled) on the same page.

So it would be safer to use the one and only global DbContext defined in Startup.cs for the whole solution and not add additional ones for each Gantt-diagram in the DAL.

Startup.cs:


Please modify your example to reach this goal!

Cheers, 
Volker

Attachment: Example_5_826c8a32.rar


LG Logeswari Gopalakrishnan Syncfusion Team June 18, 2020 06:30 PM UTC

Hi Volker, 
 
Please find the below response. 
 
Query 1: When upgrading today to v18.1.0.57 my example (and the example you provided) doesn't work any longer: 
So all BatchUpdates within CustomAdaptor are broken (try to modify an existing task, change dates or add a child via context menu). 
There seems to be a bug within the latest release v18.1.0.57. 
 
Ans:  We are also able reproduce the issue with latest version . We will provide further details on June 19, 2020. 
 
 
 
Query 2: hardcoded DB connection 
 
Ans:  
 
We have to include appsettings.json file and need to read that connection from OnConfiguring method of context file. Then In appsettings file need to add connection string. Please find the below code snippet.  
   
   
[masterContext.cs]  
   
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
        {  
            if (!optionsBuilder.IsConfigured)  
            {  
   
                IConfigurationRoot configuration = new ConfigurationBuilder()  
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)  
            .AddJsonFile("appsettings.json")  
            .Build();  
                optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));  
            }  
        }  
   
[app.settings.json]  
   
{  
    
  "ConnectionStrings": {  
    "DefaultConnection": "Server=(localdb)\\MSSQLLOCALDB; Database=MT; Trusted_Connection=True; MultipleActiveResultSets=true"  
  }  
}  
   
   
Please refer the following stack overflow link:  
 
 
Please find the below sample link:  
   
 
 
 
Query 3: Multiple DbContexts 
 
Ans: We will provide further details on June 19, 2020. 
 
Please let us know, if you need further details on this. 
 
Regards, 
Logeswari G 



LG Logeswari Gopalakrishnan Syncfusion Team June 23, 2020 12:55 PM UTC

Hi Volker 
  
Sorry for the inconvenience caused. 
 
Please find the below response.  
 
Query 1When upgrading today to v18.1.0.57 my example (and the example you provided) doesn't work any longer:  
So all BatchUpdates within CustomAdaptor are broken (try to modify an existing task, change dates or add a child via context menu).  
There seems to be a bug within the latest release v18.1.0.57.  
 
The reported issue was fixed in the latest version. Can you please upgrade Gantt packages to the latest version v18.1.0.59 and check? 
 
We have prepared sample with updated packages. Please find it from below sample link. 
 
 
Query 3: Multiple DbContexts  
 
We have achieved this by creating CustomAdaptor as component and created this from DataAdaptor class and can access this service using Service property. Please find the below code snippet. 
 
[Index.razor] 
 
<SfGantt ID="GanttExport" TValue="Table" Height="450px" Width="700px"> 
    <SfDataManager Adaptor="Adaptors.CustomAdaptor"> 
        <CustomComponent></CustomComponent> 
    </SfDataManager> 
</SfGantt> 
 
[CustomComponent.razor] 
 
 
@inherits DataAdaptor<masterContext> 
 
@code { 
 
    // Performs data Read operation 
    public override object Read(DataManagerRequest dm, string key = null) 
    { 
         
        int count = Service.Table.Cast<Table>().Count(); 
        return dm.RequiresCounts ? new DataResult() { Result = Service.Table, Count = count } : (object)Service.Table; 
    } 
 
    // Performs CRUD operation 
    public override object BatchUpdate(DataManager dm, object changedRecords, object addedRecords, object deletedRecords, string keyField, string key, int? dropIndex) 
    { 
        List<Table> addRecord = addedRecords as List<Table>; 
        List<Table> changed = changedRecords as List<Table>; 
        List<Table> deleteRecord = deletedRecords as List<Table>; 
        if (changed != null) 
        { 
            for (var i = 0; i < changed.Count(); i++) 
            { 
                var value = changed[i]; 
                Table result = Service.Table.Where(or => or.Id == value.Id).FirstOrDefault(); 
                result.Id = value.Id; 
                result.Name = value.Name; 
                result.Sdate = value.Sdate; 
                result.Edate = value.Edate; 
                result.Duration = value.Duration; 
                result.Progress = value.Progress; 
                Service.SaveChanges(); 
            } 
       } 
        if (deleteRecord != null) 
        { 
            for (var i = 0; i < deleteRecord.Count(); i++) 
            { 
                Service.Table.Remove(Service.Table.Where(or => or.Id == deleteRecord[i].Id).FirstOrDefault()); 
                Service.SaveChanges(); 
            } 
        } 
        if (addRecord != null) 
        { 
            for (var i = 0; i < addRecord.Count(); i++) 
            { 
                Service.Table.Add(addRecord[i] as Table); 
                Service.SaveChanges(); 
            } 
        } 
        return (new { addedRecords = addRecord, changedRecords = changed, deletedRecords = deleteRecord }); 
    } 
} 
 
 
And in FetchData.razor file we have conventional access to the database using masterContext as we normally inject things as a service. Please find the below code snippet. 
 
[FetchData.razor] 
 
@using GanttEF.Models  
 
@if (Table is null) 
{ 
    <p><em>Loading...</em></p> 
} 
else 
{ 
    <table class="table table-striped"> 
        <thead> 
            <tr> 
                <th>Id</th> 
                <th>Name</th> 
                <th>Sdate</th> 
                <th>Edate</th> 
                 
            </tr> 
        </thead> 
        <tbody> 
            @foreach (var task in Table) 
            { 
            <tr> 
                <td>@task.Id</td> 
                <td>@task.Name</td> 
                <td>@task.Sdate</td> 
                <td>@task.Edate</td> 
            </tr> 
            } 
        </tbody> 
    </table> 
} 
 
@code{ 
 
    [Inject] 
    protected masterContext db { get; set; } 
 
    IQueryable<Table> Table; 
 
    protected override void OnInitialized() 
    { 
        Table = db.Table; 
    } 
} 
 
Now we can use this Context for whole solution, no need to create addition Context. We have modified our sample for this. Please find the below sample link. 
 
 
Please let us know, if you need further details on this. 
 
Regards, 
Logeswari G 



VO Volker June 24, 2020 10:38 AM UTC

Hi Logeswari,

very nice solution.
Thank you very much!

Cheers,
Volker


LG Logeswari Gopalakrishnan Syncfusion Team June 25, 2020 05:06 AM UTC

Hi Volker, 
 
Most welcome.    
Please get back to us if you would require further assistance on this.   
  
Regards,   
Logeswari G  



VO Volker July 3, 2020 09:44 AM UTC

Hi Logeswari,

how are you doing?
Thank you so much for your help so far.
Anyway, some strange things are happening right now. Possibly there is a bug in your component:


1) Async

Concerning CustomAdaptor and SQL-Server (GanttEF2092535159.zip), this is no bug, just an suggestion.

Do you think it makes sense to upgrade your CustomComponent.razor-component, so it gets thread-safe (ReadAsync BatchUpdateAsync)?
If so, could you provide an example?

2) Baselines

First of all, thank you for this sample link: https://www.syncfusion.com/downloads/support/directtrac/general/ze/GanttEF2092535159 

Based on your solution I made a test and found a strange display-behaviour of baselines

This is my implemention:





Let's reproduce the behaviour...


Example A)


This is my SQL-data, pretty straight forward:
Basically I copied StartDate to BaselineStartDate and EndDate to BaselineEndDate:



Then we wire things up like:



This is the output using your CustomComponent.razor.
Please mind the orange parent tasks' base-line dates and compare it to SQL data!



Zooming in shows it even better:
Some tasks' baseline-dates are shifted to the right, although SQL-data is still correct and unchanged (SSMS).




Example B)


We can even simplify the behaviour.
Lets use the same SQL-data-source (!) for both StartDate/BaselineStartDate and EndDate/BaselineEndDate:



Orange things do not match!



3) IDs

I've observed a strange behaviour concerning Ids of tasks.
Please have a look at the demo video, which shows the problem clearly.

  • First I refresh the table (SSMS, right side) to show the actuaI IDs of all rows before we start the test.
  • Then I insert a new task (Gantt-diagram) by clicking on an existing task, e.g. ID 3025 (left side).
  • Then we get a new row showing ID 4048 in the diagram.
  • Then I refresh the table in SSMS and we see there is a new recordset on the bottom with an ID 5076.
  • Finally I refresh the diagram using MyGannt.Refresh() and only then the new task is displayed with its correct ID 5076. 
Besides of the strange behaviour, the problem here is, that there is no dedicated event which gets fired after inserting a new task.
So I'm not able to auto-refresh the gannt-diagram (MyGannt.Refresh()) after inserting things using the context menu or the dialog.
Of cause we cannot use OnActionComplete, cause this event is not fired exclusively when adding things or closing the context menu.

fyi:





4) Add a final task

Last but not least I found out that it's not possible to add a task using context-menu (1) or taskmenue item Add (2) of the very last task at the bottom (the one with the highest ID number).
When I press e.g. "Add Below" nothing happens. This only doesn't work with the very last rendered task of a diagram (in this case ID4047), on all other tasks this problem does not occure.



Any idea?

Cheers,
Volker

Attachment: EFCoreIdendityProblem_d7ef2ccb.zip


PP Pooja Priya Krishna Moorthy Syncfusion Team July 3, 2020 03:17 PM UTC

Hi Volker, 
We have created a new incident under your account. Please use your direct trac account for further follow up. 
 
Regards, 
Pooja K. 


Loader.
Up arrow icon