How to set this from Entity Framework using Linq - c#

A while back, I fell into the fat controller trap when I was first working with MVC. My first app used EF4 to make all the models I needed. I just put all my logic into my controller actions. While it worked, it's definitely not the best practice way. To do it the right way I started trying to build my models based on my EF objects in an effort to follow the skinny controller concept.
I've run into a roadblock in trying to find the best way to populate my models. Is there a way to run a LINQ query and have it populate your model without having to iterate through the properties to set to another class?
Something like this:
// from EF model built from database
public class MyEFObject
{
public int ID {get; set;}
public string Name {get; set;}
public string Title {get; set;}
}
public class MyObjectModel : MyEFObject
{
private Entities _data = new Entities();
public MyObjectModel(int? id)
{
if(id.HasValue) // get an existing record
{
this = _data.MyEFObjects.Where(m => m.ID.Equals(id)).Single();
// or populate right out of the query
_data.MyEFObjects.Where(m => m.ID.Equals(id))
.Select(o => new {
this.ID = o.ID,
this.Name = o.Name,
this.Title = o.Title
});
}
else
{
// set defaults for a new MyObjectModel
}
}
public void Save()
{
// takes the current object and saves changes
}
}
I know you can add a function to the EF Entity object, but I like having the option to Create or Update all tied up in one call (Save method). I don't see the point messing with a model if I have to essentially recreate what I already have from my EF Object. If I simply have a method on a class that accepts a populated object, the concept of a usable model for my views is negated.

Slauma is right. LINQ to Entities won't accept it. I tried a couple of versions of what was posted and I only found my self with a kludgy mess. I got it to the point where I could set instance values, but by then EF wouldn't register a change had been made and defeating the whole purpose. There may be a way to do this, but as of now, the steps to make it work seem to be overkill.
I ended up with something like this:
public class MyObjectModel : MyEFObject
{
public void Save(int? id, MyObjectModel model)
{
var data = new Entities();
MyEFObject foo;
if(id.HasValue)
{
foo = data.MyEFObjects.Where(e => e.ID.Equals(id.Value)).Single();
}
else
{
foo = new MyEFObject();
}
foo.Name = model.Name;
foo.Title = model.Title;
if(!id.HasValue)
{
data.MyEFObjects.AddObject(foo);
}
data.SaveChanges();
}
}
I didn't want to have to work with two instances of my model, but it works and I have my lean controller action.

What you could do is have a domain model, ef model and and adapter. I think this keeps the code pretty clean and nicely separates the mapping logic.
//Domain model to decouple from EF
public class MyObjectModel
{
public int ID {get; set;}
public string Name {get; set;}
public string Title {get; set;}
}
//Auto generated Entity Framework class
public class MyEFObject
{
public int ID {get; set;}
public string Name {get; set;}
public string Title {get; set;}
}
//Adapter responsible for mapping your data to your domain model
public class MyObjectModelAdapter : MyEFObject
{
public MyObjectModelAdapter(MyEFObject entity)
{
if(entity != null)
{
this.ID = entity.ID;
this.Name = entity.Name;
this.Title = entity.Title;
}
else
{
// set defaults for a new MyObjectModel
}
}
}
Then the basic usage would be:
new Entities().MyEFObjects.ToList().Select(x => new MyObjectModelAdapter(x));
OR
new MyObjectModelAdapter(new Entities().MyEFObjects.FirstOrDefault(x => x.ID.Equals(objectId)));
If you specifically require a list of MyObjectModel then you could do the following:
new Entities().MyEFObjects.ToList().Select(x => new MyObjectModelAdapter(x) as MyObjectModel);
OR
new MyObjectModelAdapter(new Entities().MyEFObjects.FirstOrDefault(x => x.ID.Equals(objectId)) as MyObjectModel;
Of course you don't want to chain your entity context together like that, it is just to show usage.

Related

Circular reference issue in webapi2

Scenario:
An Intern can learn multiple technologies
db design
ef view
Result
controller code:
private InternEntities db = new InternEntities();
// GET: api/Interns
public IQueryable<Intern> GetInterns()
{
return db.Interns;
}
What am i doing wrong here?
This is an expected error, and the reason is because your types reference each other like an Infinity Mirror. In order to solve the problem, you have several options.
1- You can develop a ViewModel and then serialize that one:
public class InternViewModel{
public int Id {get; set;}
public String Name {get; set;}
public List<String> Tehcnologies {get; set;}
}
2- You can select the properties that you need when returning the entity in your actions:
public async Task<List<Technology>> Get() {
var data = dbContext.Set<Technology>().Select(x=> new Technology{
Id = x.Id,
Name = x.Name,
Intern= new Intern {
Id = x.Technology.Id,
Name = x.Technology.Name,
Technologies = null
}
});
return await data.ToListAsync();
}
3- Load only the what you need which is known as Explicit Loading.

ASP, MVC, EF: How to map POST values from Html.ListBoxFor<T> to EF Entity Navigation Property

I am new to stackoverflow! Please forgive me if I do something wrong.
I have now searched around hours for a solution, unsuccessfully...
Background:
EF Model "Role":
public class IdItem {
[Key]
public Int32 ID { get; set; }
}
public class NamedItem : IdItem {
public String Name { get; set; }
}
public class Right : NamedItem {
...
}
public class Role : NamedItem {
/* [1] */ public ICollection<Int32> RightIDs { get; set; } // <-- Key-Collection of Nav-Prop
/* [2] */ public virtual ICollection<Right> Rights { get; set; } // <-- Nav-Prop
...
}
View "Roles.cshtml" containing:
var sliRights = /* All available Rights as SelectListItem { Value = Right.ID } */
var rightsAtts = new { #class = "form-control selectpicker", multiple = true };
#Html.ListBoxFor(x => x.RightIDs, sliRights, rightsAtts)
This way, the ID's of all selected Rights are correctly stored in the "RightIDs" property of class "Role".
Q: However, how can I tell EF to map the Keys stored in "RightIDs" to synchronously (vice-versa) use it with the "Rights"-Property (see [2])?
I guess something like [ForeignKey("RightIDs")] as att. for [2] (tried, not working)?
Update
For example something like:
public class Role : NamedItem {
public ICollection<Int32> RightIDs { get; set; }
[ForeignKey("RightIDs")] // <-- normally for non-collections, but tried -> not working at all
public virtual ICollection<Right> Rights { get; set; }
...
}
Currently, I am using an interface for class "Role" implementing a method to read all IDs from "Rights" into "RightIDs" and another method to perform the way back, both using DbContext as parameter, invoked by responsible controller:
public interface IScalarEntity {
void ToScalarProperties(DbContext context);
void FromScalarProperties(DbContext context);
}
Ugly when using this current approach for at least 20 upcoming entity classes...
Edit
As I now have researched, there is no sense to get EF to use a ICollection where T is a primitive type, even if T is a type equal to an key property of another entity Type.
Now going to use the workaround for this kind of problem.
Thanks to all contributors!
What you are doing looks really weird. First of all, you are mixing Database logic with ViewModel logic, which is a bad practice. Do you really need the IDs for the collection of Rights, when you can just get them from the Right object?
You can remove the RightIds property from your Role class and use a simple select statement in the controller:
var rights = role.Rights.ToList().Select(r => new SelectListItem
{
Value = r.ID.ToString(),
Text = r.Name
});
return View(rights);
Now in the view you will have a model with SelectListItems, which you can use with many of the List controls available in Razor.

Persisting the state pattern using Entity Framework

I am currently developing a project in MVC 3. I've separated my concerns so there are projects such as Core, Repository, UI, Services etc. I have implement the Repository, UnitOfWork and most importantly the State pattern.
I am using Entity Framework 4.3 to persist my data and I have come across a rather annoying situation involving the persistence of the current state. Below are some class examples:
public class Request
{
public int RequestId { get; set; }
public State CurrentState { get; set; }
}
public abstract class State
{
[Key]
public string Name {get; set;}
public virtual void OpenRequest(Request request)
{}
public virtual void CloseRequest(Request request)
{}
}
public class RequestIsOpenState : State
{
public RequestIsOpenState()
{
this.Name = "Open";
}
public override void CloseRequest(Request request)
{
request.CurrentState = new RequstIsClosedState();
}
}
public class RequestIsClosedState : State
{
public RequestIsClosedState()
{
this.Name = "Closed";
}
public override void OpenRequest(Request request)
{
request.CurrentState = new RequstIsOpenState();
}
}
Using the above example I will get a primary key violation exception because it tries to create a NEW state in the States table.
Because the state change is done within the domain layer, I can't just 'get' the state from the repository and set it using the foreign key by doing something like this:
Request request = unitOfWork.RequestRepository.Find(1);
request.CurrentState = unitOfWork.StateRepository.Find("Closed");
I'm aware I have the option of not mapping the state property, and persist a string property in the request class and then convert them back and forth through a factory on a get and set when the entity is hydrated (see this answer).
All I want to do is persist the state class, so when the request is returned I can access the state methods immediately without having loads of EF stuff polluting my domain layer just to handle one persistence issue. Another benefit of which would be it gives me the added bonus of having a table in SQL to query against known states.
I think you can improve it by caching the State instances creating it only once, to avoid making the list each time and avoid the foreach:
public static class StateFactory
{
private static Dictionary<string, State> statesCache = FindAllDerivedStates();
public static State GetState(string stateTypeName)
{
return statesCache[stateTypeName];
}
private static Dictionary<string, State> FindAllDerivedStates()
{
var derivedType = typeof(State);
var assembly = Assembly.GetAssembly(typeof(State));
return assembly.GetTypes().Where(t => t != derivedType && derivedType.IsAssignableFrom(t))
.Select(t => (State)Activator.CreateInstance(t))
.ToDictionary(k => k.Name);
}
}
I've made some progress by simplifying the factory back to basics and by implementing it in such a way that you would never really know that a factory is being used. Although It's not what I was looking for, it is so refined and streamlined the only downside is I still don't have a list of ALL states within the SQL database, there are however many possible work arounds for this. Anyway... my compromise:
The State Factory:
public static State GetState(string stateTypeName)
{
var list = FindAllDerivedStates();
dynamic returnedValue = new NullState();
foreach(var state in list)
{
if(state.Name == stateTypeName) returnedValue = (State)Activator.CreateInstance(state);
}
return returnedValue
}
private static List<Type> FindAllDerivedStates()
{
var derivedType = typeof(State);
var assembly = Assembly.GetAssembly(typeof(State));
return assembly.GetTypes().Where(t => t != derivedType && derivedType.IsAssignableFrom(t)).ToList();
}
Now the request needs two properties, a persisted string and a State class. Make sure the State class is not mapped.
public class Request
{
public string StateString { get; set; }
[NotMapped] or [Ignore]
public State CurrentState
{
get
{
return StateFactory.GetState(this.StateString);
}
set
{
this.State = value.GetType().Name;
}
}
}
Now because of the new simplistic implementation, saving the state is as easy as;
request.CurrentState = new OpenState();
and getting the state will always return the methods. Without any extra work you can return an entity and excess the properties. For example if you want output the public string;
request.CurrentState.StateName;
Now I've still got to implement a little work around to add a list of states to my SqlDb but that's not the end of the world. It seems this is the only solution. Or should I say best solution. I'll keep my eyes peeled for a better version.

How to combine MVC4 with EF5 Db First separate project

In my solution I have two projects. One is the main MVC4 project. The other is a DataLayer project which contains an Entity Framework 5 edmx model generated from an existing DB (and maybe some Repositories later).
The problem is that the pocos EF5 generates sits in the DataLayer project. But I need them inside the Models folder in the MVC4 project.
I want the seperate DataLayer project to increase abstraction and separation of concerns, but I can't figure out how to put those two pieces together.
(I thought to maintain another layer of pocos in the Models folder but this dose not seems right)
I have my projects separated into two as you describe.
I thought to maintain another layer of pocos in the Models folder but this dose not seems right
I think you will find you will build this layer eventually.
Here's two projects Project.Data and Project.Web. Project.Web has a project reference to Project.Data.
Project.Data.Models: Entities
Project.Web.Models: DTOs, ViewModels
My views never directly reference Entities. I will map Entities to DTOs or ViewModels using AutoMapper. This happens in my services which sits in Project.Web under its own namespace. My services never return Entity types and my views use only ViewModels.
interface IFooService
{
FooDTO GetFoo(int id);
}
public class FooService : IFooService
{
public FooDTO GetFoo(int id)
{
var foo = dbContext.Foo.Where(f => f.Id == id).Select(f => new FooDTO {
Bar = f.Bar,
Blah = f.Blah
}).FirstOrDefault();
// I let AutoMapper take care of the mapping for me
var foo = Mapper.Map<FooDTO>(dbContext.Foo.Where(f => f.Id == id).FirstOrDefault());
return foo;
}
}
Controller Action:
public ActionResult FooDetails(int id)
{
FooViewModel foo = Mapper.Map<FooViewModel>(fooService.GetFoo(id));
return View(foo);
}
Edit:
Added anther model layer to map Entity => DTO => View Model
This is the job of the repository. Create DTO classes to hold view friendly models and use the repository to call your data layer and assemble the dto. The dtos can then be built specifically for being returned to the client, including any serialization or display decorations, etc. Nothing complicated here.
I think some people's first reaction is "I'm duplicating my effort if I have to create these classes" but you're really not as these classes serve a different purpose which is exactly what you're saying, separation of concerns.
public MyViewModel // model that is bound to the view
{
private UserRepository _userRepo;
public EmployeeDto ActiveUser {get;set;}
public MyViewModel()
{
_userRepo = new UserRepository();
LoadActiveUser();
}
private void LoadActiveUser()
{
var userId = (int)HttpContext.Current.Session["activeUser"] ?? 0;
if(userId > 0)
{
ActiveUser = _userRepo.GetEmployee(userId);
}
}
}
public UserRepository
{
private SomeEntityReference1 _myDal1;
private SomeEntityReference2 _myDal2; // maybe you need to make some other data layer call in order to fill this object out
public UserRepository()
{
_myDal1 = new SomeEntityReference1 ();
_myDal2 = new SomeEntityReference2 ();
}
public EmployeeDto GetEmployee(int id)
{
var empDto = new EmployeeDto();
// get employee
var dalEmpResult = _myDal.Employees.FirstOrDefault(e => e.EmployeeId == id);
empDto.FirstName = dalResult.FName;
empDto.LastName = dalResult.LName;
empDto.Id = dalResult.EmployeeId;
// get employee department info
var dalDeptResult = _myDal2.Departments.FirstOrDefault(d => e.DepartmentId == dalEmpResult.DeptartmentId);
empDto.DepartmentName = dalDeptResult.Name;
return empDto;
}
}
// client friendly employee object
[DataContract(Name="Employee")]
public class EmployeeDto
{
public int Id {get; internal set;}
[DataMember(Name="fname")]
[DisplayName("Employee First Name:")]
public string FirstName {get;set;}
[DataMember(Name="lname")]
[DisplayName("Employee Last Name:")]
public string LastName {get;set;}
public int DeptId {get;set;}
[DataMember(Name="dept")]
[DisplayName("Works at:")]
public string DepartmentName {get;set;}
}
The only reason I show two different EF references here (your database entity schemas) is just to illustrate that this would be your opportunity to do any "additional" processing before returning a FINISHED dto, ready for consumption.

Custom key generation in RavenDB

I have sets of entities all of them are derived from abstract class
public abstract class NamedEntity : INamedEntity
{
#region Public Properties
public string Description { get; set; }
public string Id { get; set; }
public string Name { get; set; }
#endregion
}
When I persist all entities I want to use Name field as a key, so I override DocumentKeyGenerator and provide such implementation:
store.Conventions.DocumentKeyGenerator = entity =>
{
var namedEntity = entity as NamedEntity;
if (namedEntity != null)
{
return string.Format("{0}/{1}", store.Conventions.GetTypeTagName(entity.GetType()), namedEntity.Name);
}
return string.Format("{0}/", store.Conventions.GetTypeTagName(entity.GetType()));
};
It works fine when I persist the list of entities for the first time, but if I want to persist them again I get an exception
PUT attempted on document 'xxxxx' using a non current etag
I just started using RavenDB, so I cannot understand what I am doing wrong?
Just a guess, but it's probably not with your key generation, but how you are storing them.
On first usage you probably have something like:
var myEntity = new MyEntity(...);
session.Store(myEntity);
...
session.SaveChanges();
That part is fine, but on subsequent usage, you should not be doing the same thing. Instead, it should be more like this:
var myEntity = session.Load<MyEntity>("myentities/foobar");
myEntity.Something = 123;
...
session.SaveChanges();
Note there is no call to .Store() when making changes. This is because the entity is "tracked" by the session, and all changes to it are automatically persisted when you call .SaveChanges()

Categories

Resources