Master Detail ,MVC Model layer code, for better professional way - c#

I am trying to write master detail entry edit in Asp.net MVC model.
Fristly, let me show you two of my model classes.
Class name = Model\OrderDetailRepository.cs
public class OrderDetailRepository : IOrderDetailRepository
{
NorthwindEntities DB = new NorthwindEntities();
public IQueryable<Order_Detail> GetOrderDetailByOrderID(int OrderID)
{
return DB.Order_Details.Where(tableX => tableX.OrderID == OrderID);
}
public void InsertOrderDetail(Order_Detail _Order_Detail)
{
DB.Order_Details.AddObject(_Order_Detail);
}
public void UpdateOrderDetail(Order_Detail _Order_Detail)
{
DB.ObjectStateManager.ChangeObjectState(_Order_Detail, EntityState.Modified);
}
public void DeleteOrderDetailByOrderID(int OrderID)
{
Order_Detail _Order_Detail = DB.Order_Details.SingleOrDefault(x => x.OrderID == OrderID);
if (_Order_Detail != null)
DB.Order_Details.DeleteObject(_Order_Detail);
}
public void DeleteOrderDetailByCurrentRecord(Order_Detail _Order_Detail)
{
DB.Order_Details.DeleteObject(_Order_Detail);
}
public void Commit()
{
DB.SaveChanges();
}
}
Class name = Model\OrderMasterReposity.cs
public class OrderMasterReposity : IOrderMasterRepository
{
NorthwindEntities DB = new NorthwindEntities();
public IQueryable<Order> GetOrderMasterByOrderID(int OrderID)
{
return DB.Orders.Where(tableX => tableX.OrderID == OrderID);
}
public void InsertOrderMaster(Order _Order)
{
DB.Orders.AddObject(_Order);
}
public void UpdateOrderMaster(Order _Order)
{
DB.ObjectStateManager.ChangeObjectState(_Order, EntityState.Modified);
}
public void DeleteOrderMaster(int OrderID)
{
Order _Order = DB.Orders.SingleOrDefault(x => x.OrderID == OrderID);
if (_Order != null)
DB.Orders.DeleteObject(_Order);
}
public void Commit()
{
DB.SaveChanges();
}
}
After all upper code, I need to invoke these two class from my controller class.
So let's assume that below code i will write at controller layer.
OrderMasterReposity _OrderMasterReposity = new OrderMasterReposity();
OrderDetailRepository _OrderDetailRepository = new OrderDetailRepository();
_OrderMasterReposity.InsertOrderMaster(new Order{....});
_OrderDetailRepository.InsertOrderDetail(new Order_Detail{....});
_OrderMasterReposity.Commit();
_OrderDetailRepository.Commit();
What my problem is the way I doing now is not so professional way.
Because I need to invoke Commit function more than one time.
So, please could anyone give me more nicer way to code at Model layer?

You can use Unit of Work, see this example:
http://iridescence.no/post/ASPNET-MVC-DataContext-and-The-Unit-of-Work-Pattern.aspx

Best thing is that u implement Repository pattern properly
Moreover, Order detail has no meaning without order so according to DDD you should not define separate repository for order_detail. i would do it something like
Order _order = OrderMasterRepository.GetOrder(orderID);
_order.Property1 = value1;
_order.Property2 = value2;
_order.Property3 = value3;
foreach(var orderDetail in _order.OrderDetails)
{
orderDetail.Property1 = avalue1;
orderDetail.Property2 = avalue2;
.
.
}
OrderMasterRepository.Commit();
above statements will save data both in order and orderDetail table.

Related

Business logic in ASP.NET Core MVC

This is my first question here so I'm really open for opinions, I searched a lot about ASP.NET Core MVC and still I don't have enough answers if I'm writing code in right way.
In many tutorials on Youtube I saw people create ASP.NET Core MVC applications with CRUD operations but there were just simple without any logic and all code was in controllers.
What if I want add some logic for example checking if my record already exists in the database? Where should I put this?
I have class Patient and I want add patient to database so I got in patient controller this :
public async Task<IActionResult> Create([Bind("PatientId,Name,Surname,Pesel")] Patient patient)
{
if (ModelState.IsValid)
{
String result = facade.Add_patient(patient);
if (result == "Patient added")
{
_context.Add(patient);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
else
{
ViewBag.Message = "Patient exists";
return View();
}
}
}
Facade is my class in model folder where I have this :
public class Facade
{
private Database_controller _context;
public Facade(Database_controller context)
{
_context = context;
}
private List<Patient> patients = new List<Patient>();
public List<Patient> Patients { get => patients; set => patients = value; }
public void updatedata()
{
patients = _context.Patients.ToList();
}
public string Add_patient(Patient patient)
{
Patient Patient = new Patient();
Patient.Name = patient.Name;
Patient.Surname = patient.Surname;
Patient.Pesel = patient.Pesel;
String if_is = addpacjent(Patient);
if (!"Is".Equals(if_is))
{
return "Patient added";
}
else
{
return "Patient exists";
}
}
public String addpacjent(Patient val)
{
bool if_is = patients.Contains(val);
if (if_is == true)
{
return ("Is");
}
else
{
patients.Add(val);
return null;
}
}
}
In Patient class is override method equals for checking
public override bool Equals(Object ob)
{
String Name = Surname;
String Name2 = ((Patient)ob).Surname;
String Pesel1 = Pesel;
String Pesel2 = ((Patient)ob).Pesel;
bool a = Name.Equals(Name2);
if (Pesel2 != "0")
{
bool b = Pesel1.Equals(Pesel2);
bool c = false;
if (a && b == true)
{
c = true;
}
return c;
}
else
return a;
}
Is it the right way? Should I have method _context.Add(patient) in Facade or controller? Where should I check existence in database?
I already have application which I want write in .NET Core in Winforms so I want use as many as possible code from Winforms classes so it's why I started coding like this in ASP.NET Core MVC
The easiest way is to use Any(),this is because Any() will return as soon as it finds a match.
Like my following:
public IActionResult Test()
{
//Simulation data:
var patient = new Patient
{
Name="AA",
SumName="AA-aa",
Pesel="23"
};
//Here you can add your conditions.
if (!_context.Patients.Any(o => o.Name == patient.Name&&o.SumName==patient.SumName&&o.Pesel==patient.Pesel))
{
_context.Add(patient);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
else
{
ViewBag.Message = "Patient exists";
return View();
};
}
Below is the demo data in my database:
Sample effect display:

How to insert/update master-detail in Entity Framework?

I'm trying to make a master-detail Web Form working with Entity Framework and performing insert and update on the same page. I'm new at EF, so I must be making a lot of mistakes here. Can you help me pointing me what's the best practices to perform insert/update on EF? What am I doing wrong here?
In this code, the "New" mode works well, but the "Edit" mode gets this error: "An entity object cannot be referenced by multiple instances of IEntityChangeTracker".
OrdersEntities ordersEntities = new OrdersEntities();
private Order myOrder
{
get { return (Order)Session["myOrder"]; }
set { Session["myOrder"] = value; }
}
public DataTable dtOrderDetails
{
get { return (DataTable)ViewState["dtOrderDetails"]; }
set { ViewState["dtOrderDetails"] = value; }
}
private string Mode
{
get { return (string)ViewState["mode"]; }
set { ViewState["_modo"] = value; }
}
private void btnSaveOrder_Click(object sender, EventArgs e)
{
if (dtOrderDetails.Rows.Count > 0)
{
using (ordersEntities)
{
using (var contextTransaction = ordersEntities.Database.BeginTransaction())
{
try
{
if (Mode == "New")
{
Order newOrder = new Order();
OrderDetails newOrderDetails;
int maxOrderNumber = ordersEntities.Order.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
ordersEntities.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(newOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = myOrder;
OrderDetails editedOrderDetails;
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
ordersEntities.Order.Attach(editedOrder);
ordersEntities.Entry(editedOrder).State = System.Data.Entity.EntityState.Modified;
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(editedOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
}
}
catch (Exception ex)
{
contextTransaction.Rollback();
}
}
}
}
}
Here is how you should approach it.
It would be best if you abstract the DbContext away, with this simple interface:
public interface IDataRepository : IDisposable
{
IDbSet<Order> Orders { get; set; }
void Save();
}
Of course, your implementation of IDataRepository is based on EntityFramework. Note that you will need to have a dataRepositoryConnection connection string in your web.config file:
public class EfDataRepository : DbContext, IDataRepository
{
public EfDataRepository() : base("dataRepositoryConnection")
{
}
public IDbSet<Order> Orders { get; set; }
public void Save()
{
this.SaveChanges();
}
}
In my experience, you also need a 'factory', which gives you a new instance of the data repository. This allows you to be the 'owner' of the instance, and you can safely dispose it. Note that the interaction with the DataContext should be minimal - you do your Unity of Work and get rid of it. Don't reuse! You will see it as an example below.
public class DataRepositoryFactory<T> where T : IDataRepository
{
private Type dataRepositoryImplementationType;
public DataRepositoryFactory(T dataRepositoryImplementation)
{
if (dataRepositoryImplementation == null)
{
throw new ArgumentException("dataRepositoryImplementation");
}
this.dataRepositoryImplementationType = dataRepositoryImplementation.GetType();
}
public T Create()
{
return (T)Activator.CreateInstance(this.dataRepositoryImplementationType);
}
}
In your controller (if it were MVC app), or Page backend (forms), it would be best if you use Microsoft Unity to get an instance of DataRepositoryFactory. For now, a manual construction would suffice too.
IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);
Also, you don't need all this Transaction/Commit stuff you have put. It should be transparent for you. EF supports it implicitly, you don't have to be explicit about it.
// See, now you are the 'owner' of the dataRepository
using (var dataRepository = this.dataRepositoryFactory.Create())
{
if (Mode == "New")
{
Order newOrder = new Order();
// This doesn't make sense. Either generate a random order number (e.g. a Guid), or just use the Order.Id as an order number, although I don't recommend it.
int maxOrderNumber = dataRepository.Orders.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
dataRepository.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
newOrder.OrderDetails.Add(newOrderDetails);
}
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = dataRepository.Orders.FirstOrDefault(o => o.Id == myOrder.Id);
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
editedOrder.OrderDetails.Add(editedOrderDetails);
}
}
dataRepository.Save();
}
Also, I am pretty sure you have setup the relation between Order and OrderDetails classes incorrectly, in your EF code-first approach.
This is just wrong:
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
If you post them here, I can fix them for you.

Using Domain Events to Update Entity in Database

I have a PurchaseOrder aggregate root which has Two methods FinalizeOrder and CancellOrder, they both record events: OrderFinaziled and OrderCancelled. I am stuck on modeling order repository, can i use those events inside repository pattern to update entity in database? i don't wont after each change to save whole aggregate root, i want to save only the field that was changed, i am using SqlClient, no ORMs.
my Aggregate root base class:
public class AggregateRootBase<TID> : EntityBase<TID>
{
public AggregateRootBase(TID id) : base(id)
{
}
private readonly List<IDomainEvent> recordedEvents = new List<IDomainEvent>();
public IEnumerable<IDomainEvent> GetEvents()
{
return recordedEvents;
}
public void MarkEventsAsProcessed()
{
recordedEvents.Clear();
}
protected void RecordEvent(IDomainEvent #event)
{
recordedEvents.Add(#event);
}
}
PurchaseOrder class (skipped most properties):
public class PurchaseOrder : AggregateRootBase<int>
{
public PurchaseOrder(int id) : base(id)
{
IsFinalized = false;
IsCancelled = false;
}
public bool IsFinalized { get; set; }
public bool IsCancelled { get; set; }
public void FinalizeOrder()
{
IsFinalized = true;
RecordEvent(new OrderFinalized(Id,IsFinalized));
}
public void CancellOrder()
{
IsCancelled = true;
RecordEvent(new OrderCancelled(Id,IsCancelled));
}
}
and repository:
public class PurchaseOrderRepository
{
void Save(PurchaseOrder purchaseOrder)
{
var events = purchaseOrder.GetEvents();
foreach (var evt in events)
{
if(evt.GetType() == typeof(OrderFinalized))
// use event args and update field using SqlCommand
else if (evt.GetType() == typeof(OrderCancelled))
// use event args and Update field Using SqlCommand
}
}
}
i also have an EventDispatcher which dispatches events (Email Notification) after AggregateRoot successful persistence.
If you want to use domain events to save your changes, you are talking about projecting events to your aggregate state. There are some tools that can help with that. What you are trying to do resembles the pattern matching, a usual feature of most functional languages. To make life easier you might want to check something like Projac. We use it for projections and it works very nicely. It also has SQL Server specific implementation.
One example that you can find there:
public class PortfolioProjection : SqlProjection
{
public PortfolioProjection()
{
When<PortfolioAdded>(#event =>
TSql.NonQueryStatement(
"INSERT INTO [Portfolio] (Id, Name) VALUES (#P1, #P2)",
new { P1 = TSql.Int(#event.Id), P2 = TSql.NVarChar(#event.Name, 40) }
));
When<PortfolioRemoved>(#event =>
TSql.NonQueryStatement(
"DELETE FROM [Portfolio] WHERE Id = #P1",
new { P1 = TSql.Int(#event.Id) }
));
When<PortfolioRenamed>(#event =>
TSql.NonQueryStatement(
"UPDATE [Portfolio] SET Name = #P2 WHERE Id = #P1",
new { P1 = TSql.Int(#event.Id), P2 = TSql.NVarChar(#event.Name, 40) }
));
}
}
Then you can initialise projector instance:
_projector = new SqlProjector(
Resolve.WhenEqualToHandlerMessageType(new PortfolioProjection()),
new TransactionalSqlCommandExecutor(
new ConnectionStringSettings(
"projac",
#"Data Source=(localdb)\ProjectsV12;Initial Catalog=ProjacUsage;Integrated Security=SSPI;",
"System.Data.SqlClient"),
IsolationLevel.ReadCommitted));
And then project events:
void Save(PurchaseOrder purchaseOrder) =>
_projector.Project(purchaseOrder.GetEvents());
You might also check the event sourcing pattern although it adds quite lot of complexity.

Implementing Strategy pattern instead of several if statements

I have this method with lot of if statements, in which I'm filtering SharePoint list based on employee position.The result is query string which is passed as parameter to another method QuerySPList.
public List<Phone> GetListOfPhones(Employee emp)
{
List<Phone> records = new List<Phone>();
string query = string.Empty;
if (emp.Positions.Count == 1 && emp.Positions[0] == "Regular")
{
query = "";// some querystring
}
if (emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector")
{
query = "";// some querystring
}
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
{
query = "";// some querystring
}
if (emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector"))
{
query = "";// some querystring
}
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}}
I've read that with implementing strategy pattern we can avoid messy code with lot of if's , however , I'm new to developing , and design patterns , so I need little help with this one,so if someone can guide me what should I do and give me advice , feel free to answer my question.I have idea , but I think that I'm going in the wrong direction.
Idea is to create interface IRoleHandler and than implement it to 3 classes , for example RoleAdmin ,RoleRegular,RoleOfficeDirector.Something like this :
public interface IRoleHandler<T>
{
string handleRole(T obj);
}
public class RoleAdmin:IRoleHandler<Employee>
{
public string handleRole(Employee emp)
{
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
{
//return some string query
}
}
}
Then idea is to create dictionary,something like this :
Dictionary<string, IRoleHandler<Employee>> strategyHandlers = new Dictionary<string, IRoleHandler<Employee>>();
strategyHandlers.Add("Admin", new RoleAdmin());
strategyHandlers.Add("Regular", new RoleRegularUser());
Firstly, I think you need to be less concerned with the 'internal structure' of the Strategy Pattern and more concerned with its design goal/use.
https://en.wikipedia.org/wiki/Strategy_pattern
the strategy pattern (also known as the policy pattern) is a software design pattern that enables an algorithm's behavior to be selected at runtime. The strategy pattern
defines a family of algorithms,
encapsulates each algorithm, and
makes the algorithms interchangeable within that family.
To me, it looks like you are actually intending to "create" a string, based on some input value.
Therefore you would likely want to use one of the Creational patterns
My suggestion would be likely be Factory Pattern, or possibly Builder pattern. (Both linked in the creational patterns link)
private string BuildQueryByEmployee(Employee emp) {
// create your builder
EmployeeQueryBuilder mBuilder = new EmployeeQueryBuilder ();
// add # of positions
mBuilder.addPositionCount(emp.Positions.Count);
// add each position
for (int i =0 ; i < emp.Positions.Count; i++ ){
mBuilder.addPosition(emp.Positions[i]);
}
// 'build' your query.
// and return the query as a string.
return mBuilder.buildQuery();
}
Then inside your EmployeeQueryBuilder class, you handle the complexity of how to build your query based on the # of positions, and what they are.
Secondly, Here are some links that might be of use to you.
https://en.wikipedia.org/wiki/Software_design_pattern (General disc. of Patterns)
https://en.wikipedia.org/wiki/Design_Patterns (The "Gang of Four" book, essentially the first book on design patterns, and worth reading a few times, and writing each pattern out by hand a few times until you are comfortable with them, this alone will boost your coding ability by 2~3x if you haven't used patterns before, The examples in this book are somewhat out of date, since its motivating factor is writing a Text Editor... but still a 'classic', well worth knowing)
http://www.cs.wustl.edu/~schmidt/POSA/POSA2/ (One of the first books on patterns that have to deal with Concurrency) (POSA = Pattern Oriented Software Architecture) (might be overkill for now, but 'good to know about' in general)
I don't think strategy pattern is what you want. I would just refactor your code to extract the ifs to another method.
public List<Phone> GetListOfPhones(Employee emp)
{
List<Phone> records = new List<Phone>();
string query = BuildQueryByEmployeePosition(emp);
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}
private string BuildQueryByEmployeePosition(Employee emp)
{
if (emp.Positions.Count == 1 && emp.Positions[0] == "Regular")
return "";// some querystring
if (emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector")
return "";// some querystring
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
return "";// some querystring
if (emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector"))
return "";// some querystring
return ""; // some default query
}
Here is a link to a great explanation of strategy pattern
(with a real example of a sort algorithm).
It's great to see that you are new to programming and is studying patterns, but
beware: using them when they are not needed is an anti-pattern.
In my opition it is better you use a kind of STATE PATTERN with COMPOSITE.
Example:
public abstract class Position
{
public abstract List<int> ListOfPhones();
}
public class Employer
{
public virtual IList<Position> CurrentPositions { get; set; }
}
public class Manager : Employer
{
public Manager(List<Position> positions)
{
this.CurrentPositions = positions;
}
public IEnumerable<int> GetNumbers()
{
foreach (Position position in this.CurrentPositions)
foreach (var number in position.ListOfPhones())
yield return number;
}
}
Code above is incomplete, just for you get the ideia.
I agree with mawalker, you don't need strategy pattern as you have the same behavior for all cases. What do you need is some kind of creational patterns. I would refactor your code using Builder and Chain of Responsibility patterns.
1) Implement base class:
public abstract class EmployeeHandler
{
private readonly EmployeeHandler _nextHandler;
protected EmployeeHandler(EmployeeHandler nextHandler)
{
_nextHandler = nextHandler;
}
public string BuildQuery(Employee emp)
{
if (CanHandle(emp))
{
return GetQuery(emp);
}
if (_nextHandler == null)
{
return string.Empty;
}
return _nextHandler.BuildQuery(emp);
}
protected abstract string GetQuery(Employee emp);
protected abstract bool CanHandle(Employee emp);
}
2) Define concrete implementations:
public class RegularEmployeeHandler : EmployeeHandler
{
public RegularEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "Regular";
}
protected override string GetQuery(Employee emp)
{
return "some regular query";
}
}
public class OfficeDirectorEmployeeHandler : EmployeeHandler
{
public OfficeDirectorEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector";
}
protected override string GetQuery(Employee emp)
{
return "some office director query";
}
}
public class AdminEmployeeHandler : EmployeeHandler
{
public AdminEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "Admin";
}
protected override string GetQuery(Employee emp)
{
return "some admin query";
}
}
public class RegularAndOfficeDirectorEmployeeHandler : EmployeeHandler
{
public RegularAndOfficeDirectorEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override string GetQuery(Employee emp)
{
return "some regular and director query";
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector");
}
}
3) And finally you main class will changed like this:
public class YouMainClass
{
private readonly EmployeeHandler _firstHandler;
public YouMainClass()
{
var regularHandler = new RegularEmployeeHandler(null);
var officeDirectorHandler = new OfficeDirectorEmployeeHandler(regularHandler);
var adminHandler = new AdminEmployeeHandler(officeDirectorHandler);
_firstHandler = new RegularAndOfficeDirectorEmployeeHandler(adminHandler);
}
public List<Phone> GetListOfPhones(Employee emp, IQueryBuilder queryBuilder)
{
List<Phone> records = new List<Phone>();
string query = _firstHandler.BuildQuery(emp);
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}
}

Entity Framework - Many to Many relationship not saving to database

I have stumbled upon a problem with Entity Framework this morning.
I have following code mapping a modified entity and saving it into database.
public Group Save(Group x)
{
using (var db = new HostContext())
{
db.Projects.Attach(x.Project);
if (x.ID != 0)
{
db.AttachableObjects.Attach(x);
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeObjectState(x, EntityState.Modified);
}
else
{
db.AttachableObjects.Add(x);
}
db.SaveChanges();
return x;
}
}
I call Save method with existing group as a parameter. Group contains one user I want to add as a member.
The method finishes successfully, however the relationship is not persisted in database.
Any help is very appreciated.
EDIT: These are my classes
class User : AttachableObject
{
...
private List<Group> memberof;
[DataMember]
[InverseProperty("Members")]
public List<Group> MemberOf
{
get { return memberof; }
set { memberof = value; }
}
...
}
class Group : AttachableObject
{
...
private List<User> members;
[DataMember]
[InverseProperty("MemberOf")]
public List<User> Members
{
get { return members; }
set { members = value; }
}
...
}
EDIT2: This is where the Save method is called
public Group AcceptInvite(int id)
{
var mapper = new InviteMapper();
var userMapper = new UserMapper();
var groupMapper = new GroupMapper();
var invite = mapper.Find(id);
if (invite != null)
{
var group = groupMapper.Find(invite.GroupID);
var user = userMapper.Find(invite.InviteeID);
group.Members.Add(user);
mapper.Delete(invite.ID);
return groupMapper.Save(group);
}
return null;
}
EDIT3: My mappers
public class GroupMapper
{
public Group Find(int id)
{
using (var db = new HostContext())
{
return db.AttachableObjects
.Include("Project")
.OfType<Group>().FirstOrDefault(x => x.ID == id);
}
}
}
The rest of the mappers is the same, only using their own tables.
You are not changing the relationship info of Project, you are only setting x to modified, relationship info must be changed explicitly.
So x.Project must have some property that points back to Group, you need to set it so the change is recorded.
I am guessing that x is resurrected via some deserialization process?

Categories