RSS

Understanding Previous Co-Workers After You have Left

So today I had an unusual email wait let me start from the beginning. I was at a company for some time and left for a new opportunity I worked with another developer to co-develop two websites with the same supporting back ends. Though we had our differences I always thought we were good associates. At my new place of work I came across a problem and remembered an open source tool that I couldn’t remember the name of so I emailed this previous co-worker wondering if he remembered the name for it and here is the message chain with names redacted and any personally identifiable information:

I just wanted the name of the public library not anything developed by Acme. Thanks for the answer though.

Alex Seabrooks

Senior Application developer *

Phone:  *

Location: *     

From: * [*]
Sent: Tuesday, September 24, 2013 1:58 PM
To: Seabrooks, Alex
Subject: RE: Hi *

Hello Alex,

Sorry, but I did not receive permission to divulge any Acme IP nor the sources used to develop Acme IP.

*name*

From: Seabrooks, Alex [*]
Sent: Monday, September 23, 2013 10:23 AM
To: ******
Subject: Hi *

Hi *****, how have you been? Had a question for you hope you don’t mind me asking you hopefully this email finds you well. A while back we implemented a message pane on the accounts search page that let you know that your last action was successful and you got the library from Google. I have tried finding it and can’t. Do you happen to remember the library address? Working on a similar issue here and would love to find it.

Alex Seabrooks

Senior Application Developer- *

Phone:  *

Location: *     

So I am confused by this but alright this comes down to where you thought you stood and where you stand with previous co-workers.Now asking the name of an open source library to me did not sound like a big deal but from what I can gather he regards it as one or maybe we did not stand where I thought we did when I left. Either way interacting with previous co-workers can sometimes uncover issues you were unaware of and maybe that’s a good thing. I personally wanted to talk about the approach I take as I would have differed in my actions 

Now contracts are a big thing both NDA’s and non-competes, make sure in your contract you are free and clear. NDA means you won’t disclose source code or the like that belongs to your company and sometimes more. So regard your personal agreements first as these override any opinion I have and this is not legal advice. Now with that out of the way, I have some rules in interacting with people I used to work with. My rule of thumb is to be cordial and helpful as the people you work with may work with you again. Nothing creates issues like someone you used to work with coming on board and there being pre-existing issues. This creates unneeded cohesion and work issues. Be nice to previous co-workers who contact you with a minor contact question or the like. Most likely they regard you in a friendly manner. If it’s nothing violating your contracts with said previous company why not help them out. John not remembering that jQuery Validation was the library you used for validation is not a big deal in my opinion and I would gladly help as it is an open source library that was not property of the company we worked for and does not disclose any specific design implementations (I.E. code). But let me get your opinion on this how do you feel should you brick wall or be cordial? Inbox me and I will approve your messages as I receive them as usual.

 

Advertisements
 
Leave a comment

Posted by on September 24, 2013 in Uncategorized

 

Repository and Unit of Work that is Fully Generic Part 3

So now for the last part the extensions to DBContext

using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;

namespace MyCompany.Repo.Generic.Extensions
{
    ///
/// Code First extensions.
    ///
    public static class DbContextExtensions
    {
        ///
/// Adds an entity (if newly created) or update (if has non-default Id).
        ///
        ///
        ///The db context.
        ///The entity.
        ///
        ///
        /// Will not work for HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).
        /// Will not work for composite keys.
        ///
        public static T AddOrUpdate(this DbContext context, T entity)
            where T : class
        {
            if (context == null) throw new ArgumentNullException("context");
            if (entity == null) throw new ArgumentNullException("entity");

            if (IsTransient(context, entity))
            {
                context.Set().Add(entity);
            }
            else
            {
                context.Set().Attach(entity);
                context.Entry(entity).State = EntityState.Modified;
            }
            return entity;
        }

        ///
/// Determines whether the specified entity is newly created (Id not specified).
        ///
        ///
        ///The context.
        ///The entity.
        ///
        ///   true if the specified entity is transient; otherwise, false.
        ///
        ///
        /// Will not work for HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).
        /// Will not work for composite keys.
        ///
        public static bool IsTransient(this DbContext context, T entity)
            where T : class
        {
            if (context == null) throw new ArgumentNullException("context");
            if (entity == null) throw new ArgumentNullException("entity");

            var propertyInfo = FindPrimaryKeyProperty(context);
            var propertyType = propertyInfo.PropertyType;
            //what's the default value for the type?
            var transientValue = propertyType.IsValueType ?
                Activator.CreateInstance(propertyType) : null;
            //is the pk the same as the default value (int == 0, string == null ...)
            return Equals(propertyInfo.GetValue(entity, null), transientValue);
        }

        ///
/// Loads a stub entity (or actual entity if already loaded).
        ///
        ///
        ///The context.
        ///The id.
        ///
        ///
        /// Will not work for composite keys.
        ///
        public static T Load(this DbContext context, object id)
             where T : class
        {
            if (context == null) throw new ArgumentNullException("context");
            if (id == null) throw new ArgumentNullException("id");

            var property = FindPrimaryKeyProperty(context);
            //check to see if it's already loaded (slow if large numbers loaded)
            var entity = context.Set().Local
                .FirstOrDefault(x => id.Equals(property.GetValue(x, null)));
            if (entity == null)
            {
                //it's not loaded, just create a stub with only primary key set
                entity = CreateEntity(id, property);

                context.Set().Attach(entity);
            }
            return entity;
        }

        ///
/// Determines whether the specified entity is loaded from the database.
        ///
        ///
        ///The context.
        ///The id.
        ///
        ///   true if the specified entity is loaded; otherwise, false.
        ///
        ///
        /// Will not work for composite keys.
        ///
        public static bool IsLoaded(this DbContext context, object id)
            where T : class
        {
            if (context == null) throw new ArgumentNullException("context");
            if (id == null) throw new ArgumentNullException("id");

            var property = FindPrimaryKeyProperty(context);
            //check to see if it's already loaded (slow if large numbers loaded)
            var entity = context.Set().Local
                .FirstOrDefault(x => id.Equals(property.GetValue(x, null)));
            return entity != null;
        }

        ///
/// Marks the reference navigation properties unchanged.
        /// Use when adding a new entity whose references are known to be unchanged.
        ///
        ///
        ///The context.
        ///The entity.
        public static void MarkReferencesUnchanged(DbContext context, T entity)
            where T : class
        {
            var objectContext = ((IObjectContextAdapter)context).ObjectContext;
            var objectSet = objectContext.CreateObjectSet();
            var elementType = objectSet.EntitySet.ElementType;
            var navigationProperties = elementType.NavigationProperties;
            //the references
            var references = from navigationProperty in navigationProperties
                             let end = navigationProperty.ToEndMember
                             where end.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne ||
                             end.RelationshipMultiplicity == RelationshipMultiplicity.One
                             select navigationProperty.Name;
            //Note: We don't check Collections. EF wants to handle the object graph so we let it.

            var parentEntityState = context.Entry(entity).State;
            foreach (var navigationProperty in references)
            {
                //if it's modified but not loaded, don't need to touch it
                if (parentEntityState == EntityState.Modified &&
                    !context.Entry(entity).Reference(navigationProperty).IsLoaded)
                    continue;
                var propertyInfo = typeof(T).GetProperty(navigationProperty);
                var value = propertyInfo.GetValue(entity, null);
                context.Entry(value).State = EntityState.Unchanged;
            }
        }

        private static PropertyInfo FindPrimaryKeyProperty(IObjectContextAdapter context)
            where T : class
        {
            //find the primary key
            var objectContext = context.ObjectContext;
            //this will error if it's not a mapped entity
            var objectSet = objectContext.CreateObjectSet();
            var elementType = objectSet.EntitySet.ElementType;
            var pk = elementType.KeyMembers.First();
            //look it up on the entity
            var propertyInfo = typeof(T).GetProperty(pk.Name);
            return propertyInfo;
        }

        private static T CreateEntity(object id, PropertyInfo property)
            where T : class
        {
            // consider IoC here
            var entity = (T)Activator.CreateInstance(typeof(T));
            //set the value of the primary key (may error if wrong type)
            property.SetValue(entity, id, null);
            return entity;
        }
    }
}

  1. Create an Entity project
  2. Add EF 5 from nugget
  3. Add an ADO.Net Entity Data Model of your database
  4. Right click on the designer and click “Add Code Generation Item”
  5. If you do not have the EF 5.x DbContect Generator installed already click online templates select the EF 5.x DbContect Generator, name it appropriately, and click add.
  6. If they do not generat off the bat open the .tt and put in the correct .edmx file name and your db context and poco classes will generate
  7. Add the connection string into the consuming app and add your entity project and the generic repo to your consuming app. Then do something like this in you BLL J
            NPM_EXTEntities context = new NPM_EXTEntities();
            IUnitOfWork efu = new UoWFactory(context).getUoW();

            IGenericRepository<Application>; 
            applications = efu.GetRepository<Application>;
 
Leave a comment

Posted by on September 4, 2013 in C#, Entity Framework, Repository, SQL, Unit Of Work

 

Tags: , , , , , ,

Repository and Unit of Work that is Fully Generic Part 2

So now we continue with the Unit Of Work Factory and the EF specific Items along with Extensions to the DB Context

UoWFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;

namespace MyCompany.Repo.Generic
{
    public class UoWFactory : IDisposable
    {
        #region Variables
        private readonly string _context;
        private readonly DbContext _DBcontext;
        #endregion

        #region Constructor(s)
        public UoWFactory(string context)
        {
            _context = context;
        }

        public UoWFactory(DbContext context)
        {
            _DBcontext = context;
        }
        #endregion

        #region Public Methods
        public IUnitOfWork GetUoW()
        {
            if (_DBcontext != null)
            {
                return new EFUnitOfWork( _DBcontext);
            }
            else
            {
                /*this will be the EL unit of work which needs to be fleshed out to 
                 * process the LINQ expressions for the IQueryable's and the like 
                 * this was beyond the scope of what I was asked to do for my project
                 * and will need to be finalized at a later time unless I have to convert from EF to EL
                 */                
               throw new NotImplementedException("EL Unit Of Work is currently unavailable");
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Protected and Private Methods
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                if (_DBcontext != null)
                    _DBcontext.Dispose();
        }
        ~UoWFactory()
        {
            Dispose(false);
        }
        #endregion

    }
}

EFRepository

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Data;
using MyCompany.Repo.Generic.Extensions;

namespace MyCompany.Repo.Generic
{
    public class EFRepository<T> : BaseGenericRepository, IGenericRepository<T> where T : class
    {
        #region Variables
        private readonly DbContext _context;
        #endregion

        #region Constructor(s)
        public EFRepository(DbContext context)
        {
            _context = context;
        }
        #endregion

        #region Public Methods

        public override Type Type
        {
            get { return typeof(T); }
        }

        public T Value { get; set; }
 
        public IQueryable<T> Get
        {
            get { return _context.Set<T>(); }
        }
 
        public IQueryable<T> GetIncluding(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _context.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }
 
        public T Find(object[] keyValues)
        {
            return _context.Set<T>().Find(keyValues);
        }
 
        public void Add(T entity)
        {
            _context.Set<T>().Add(entity);
        }
 
        public void Update(T entity)
        {
            var entry = _context.Entry(entity);
            if (entry.State == EntityState.Detached)
            {
                _context.Set<T>().Attach(entity);
                entry = _context.Entry(entity);
            }
            entry.State = EntityState.Modified;
        }
 
        public void AddOrUpdate(T entity)
        {
            //uses DbContextExtensions to check value of primary key
            _context.AddOrUpdate(entity);
        }
 
        public void Delete(object[] keyValues)
        {
            //uses DbContextExtensions to attach a stub (or the actual entity if loaded)
            var stub = _context.Load<T>(keyValues);
            _context.Set<T>().Remove(stub);
        }
        
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Protected and Private Methods
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                if (_context != null)
                    _context.Dispose();
        }

        ~EFRepository()
        {
            Dispose(false);
        }
        #endregion

    }
}

EFUnitOfWork

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Data;

namespace MyCompany.Repo.Generic
{
    public class EFUnitOfWork : IUnitOfWork
    {
        #region Variables
        private readonly DbContext context;
        private readonly Dictionary<Type, BaseGenericRepository> _dictionary;
        #endregion

        #region Constructor(s)
        public EFUnitOfWork(DbContext context)
        {
            this.context = context;
            this.context.ChangeTracker.DetectChanges();
            this.context.Configuration.AutoDetectChangesEnabled = true;
            _dictionary = new Dictionary<Type, BaseGenericRepository>();
        }
        #endregion

        #region Repository Collection Methods
        
            private void Put<T>(EFRepository<T> item) where T : class
            {
                _dictionary[typeof(T)] = item;
            }

            private EFRepository<T> Get<T>() where T : class
            {
                if (_dictionary.ContainsKey(typeof(T)))
                {
                    return _dictionary[typeof(T)] as EFRepository<T>;
                }
                else
                {                    
                    EFRepository<T> efr = new EFRepository<T>(context);
                    Put<T>(efr);
                    return efr;
                }                
            }

        #endregion

        #region Public Methods


        public IGenericRepository<T> GetRepository<T>() where T : class
        {            
            return Get<T>();
        }

        public void SaveChanges()
        {
            context.SaveChanges();
        }

        public void RollBack()
        {
            //detect all changes (probably not required if AutoDetectChanges is set to true)
            context.ChangeTracker.DetectChanges();

            //get all entries that are changed
            var entries = context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged).ToList();

            //try to discard changes on every entry
            foreach (var dbEntityEntry in entries)
            {
                var entity = dbEntityEntry.Entity;

                if (entity == null) continue;

                if (dbEntityEntry.State == EntityState.Added)
                {
                    //if entity is in Added state, remove it. (there will be problems with Set methods if entity is of proxy type, in that case you need entity base type
                    var set = context.Set(entity.GetType());
                    // now lets actually remove it
                    set.Remove(entity);
                }
                else if (dbEntityEntry.State == EntityState.Modified)
                {

                    context.Entry(dbEntityEntry.Entity).CurrentValues.SetValues(context.Entry(dbEntityEntry.Entity).OriginalValues);
                    //may also need to set back to unmodified -
                    //I'm unsure if EF will do this automatically
                    context.Entry(dbEntityEntry.Entity).State = EntityState.Unchanged;                    
                }
                else if (dbEntityEntry.State == EntityState.Deleted)
                    //entity is deleted... change its values back to default and set it to unmodified
                    context.Entry(dbEntityEntry).CurrentValues.SetValues(context.Entry(dbEntityEntry).OriginalValues);
                    dbEntityEntry.State = EntityState.Unchanged;
            }
        }

        public void Dispose()
        {
            context.Dispose();
        }

        #endregion

    }
}

 In part 3 we will show the extension methods for DBContext and go into useage.


 
Leave a comment

Posted by on September 4, 2013 in C#, Entity Framework, Repository, SQL, Unit Of Work

 

Tags: , , , , ,

Repository and Unit of Work that is Fully Generic Part 1

So I have been working on creating a generic repository and unit of work and man there are no full examples out there. The following code I wrote is a combination of mine and other peoples work that I referenced combined into what I like to think is a very strong generic unit of work and repository that does not need to have the repositories declared in the unit of work :). Also on your POCO’s if you want EF to throw conncurency errors for Store Wins instead of Client Wins use the timestamp decorator from System.ComponentModel.DataAnnotations. Here is the code

IUnitOfWork

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCompany.Repo.Generic
{
    public interface IUnitOfWork : IDisposable
    {
        IGenericRepository<T> GetRepository<T>() where T : class;
        void SaveChanges();
        void RollBack();
    }
}

IGenericRepository

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace MyCompany.Repo.Generic
{
    public interface IGenericRepository<T> where T : class
    {
        IQueryable<T> Get { get; }
        IQueryable<T> GetIncluding(params Expression<Func<T, object>>[] includeProperties);
        T Find(object[] keyValues);
        void Add(T entity);
        void Update(T entity);
        void AddOrUpdate(T entity);
        void Delete(object[] keyValues);
    }
}

Base Generic Repository

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCompany.Repo.Generic
{
    /// <summary>
    /// This class is used to allow you to store the repositories in a collection so they stay in scope 
    /// of the unit of work since in .net there is no class<?> like there is in Java
    /// </summary>
    public abstract class BaseGenericRepository
    {
        public abstract Type Type { get; }
    }
}

Ther rest is to come in part 2

 
1 Comment

Posted by on September 4, 2013 in C#, Entity Framework, Repository, SQL, Unit Of Work

 

Tags: , , , , , ,

How to add Controls Dynamically and Have Them Persist Through Postback

So the need arises and you need to add controls on demand in my case it is so that a user can add multiple accounts to pay by clicking a button. Here is how to do it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
    private const int controlCount = 1;

    protected void Page_Init(object sender, EventArgs e)
    {
        
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            foreach (HtmlGenericControl item in MyControlState)
            {
                PlaceHolder1.Controls.Add(item);
            }
        }
    }

    private int _CurrentControlcount
    { 
        get
        {
            int _currentControlcount = object.Equals(Session["_currentControlcount"], null) ? controlCount : (int)Session["_currentControlcount"];
            if (Object.Equals(_currentControlcount, null))
            {
                _currentControlcount = new int();
                _currentControlcount = 0;
                Session["_currentControlcount"] = _currentControlcount;
                
            }
            return _currentControlcount;
        }        
        set
        {
             Session["_currentControlcount"] = value;
        }
    }

    private List<HtmlGenericControl> MyControlState
    {
        get
        {
            List<HtmlGenericControl> myControlState = (List<HtmlGenericControl>)Session["myControlState"];
            if (object.Equals(myControlState,null))
            {
                myControlState = new List<HtmlGenericControl>();
            }

            return myControlState;
        }
        set
        {
            Session["myControlState"] = value;
        }
        
    }

    protected void btnAddControls_Click(object sender, EventArgs e)
    {
        int count = MyControlState.Count();
        HtmlGenericControl myGenericUL = new HtmlGenericControl("ul");
        HtmlGenericControl myGenericLI = new HtmlGenericControl("li");
        TextBox tbx = new TextBox() { Text = string.Format("Textbox{0}", count), ID = string.Format("Textbox{0}", count) };
        myGenericLI.Controls.Add(tbx);
        myGenericUL.Controls.Add(myGenericLI);
        PlaceHolder1.Controls.Add(myGenericUL);
        List<HtmlGenericControl> setup = MyControlState;
        setup.Add(myGenericUL);
        MyControlState = setup;
        _CurrentControlcount = _CurrentControlcount + 1;
    }

}

and the html

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!-- use the default masterpage from microsoft or whatever you like -->
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>
        Welcome to ASP.NET!
    </h2>
    <p>
        To learn more about ASP.NET visit <a href="http://www.asp.net" title="ASP.NET Website">www.asp.net</a>.
    </p>
    <p>
        You can also find <a href="http://go.microsoft.com/fwlink/?LinkID=152368&amp;clcid=0x409"
            title="MSDN ASP.NET Docs">documentation on ASP.NET at MSDN</a>.
    </p>
    
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true"></asp:ScriptManager>
    <asp:PlaceHolder ID="PlaceHolder1" runat="server" >

    </asp:PlaceHolder>
    <br />
    <asp:Button ID="btnAddControls" runat="server" Text="Add Control" 
        onclick="btnAddControls_Click" />

</asp:Content>

now I know that the code is rough and I am touching it up as we speak i really meant to hit draft not publish but it will be ok

 
Leave a comment

Posted by on May 20, 2013 in Uncategorized

 

Why You Would Ever Use SOAP Over REST (Why I Never Learned REST Web Services)

Most of my career I have dealt with Banking, High Integrity, HIPPA, PCI, and Financial data. When dealing with these kinds of data the main things you need to be worried about are:

  1. Ability to perform distributed transactional functions
  2. Verification of identity through intermediary (not just point to point IE. SSL)
  3. Standard implementation of data integrity and data privacy.

For the above data types the three things above aren’t a option you must implement them and SOAP does all three with WS-Security, WS-AtomicTransaction, and WS-ReliableMessaging.

REST is limited by the HTTP protocol itself and the stateless nature of the web for things like transactions and expects you to handle errors in communication. SOAP on the other hand provides two-phase commit across distributed transactional resources, has successful/retry logic built in, non-repudiation through signed messages, and support for signature formats: SAML Certificates- Kerberos tickets-x.509 certificates. SOAP also has the ability to encrypt the messages itself and a few other nice features such as attaching security tokens built in natively. This combination of things along with proper use of ciphers, formats, and algorithms leads to what is called end to end security which is a requirement for the above mentioned data tpyes to be compliant with industry standards and in some cases federal law. For anyone who does not need these things REST is great but for secure data, strong typing, and support for numerous security mechanisms to ensure safe use and reliable data you use SOAP.

 
 

Tags: , , , , , ,

Problems With IE 10 and X-UA-Compatible meta tag

So as a lot of you prob know Microsoft has pushed IE 10 in a KB release on Apr 9th. For some of us this has caused a headache because there is a issue with how the

<meta http-equiv="X-UA-Compatible" content="IE=8" />

tag is interpreted and we use this as developers sometimes for SharePoint, Dynamics CRM, and using some older libs in web apps for certain pages. Now the answer for us was removing this tag as it was for code I rewrote long ago. But lets get to the underlying cause. so if you make a demo page and place this tag in the meta, then put two text boxes and attach js validate 1.1 to the boxes and make one required and one not. When you submit the page you will see both will show up as required. So lets hit F12 and see whats up with the rendering.

OMGS what is this (credit to Corey Peters his image looked better than mine so I stole it.)

So as you see in the screen cap above IE 10 is getting the Doc Mode correct but not the Browser Mode. This is great for those who have been pushing Microsoft for proper web standards but man they could have put out some warning instead of putting IE 10 in a KB so it would have been more apparent when skimming over upcoming WSUS that IE 10 was coming. This causes issues in rendering as the doc type clashes with the browser mode in how the page is processed/handled by IE breaking quite a few JS libs.

If you are experiencing these issues you can either remove the tag if it is not needed, detect the browser and display a warning, or look at your code and fix the issues that are holding it back from IE 10. In the short term you also can do the following.

1. Use a loose instead of strict doctype
2. Validate your markup and make sure it is correct
3. Add a site to compatibility mode view settings
4. change tag to

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >

If that does not work you may have a long road ahead. Stay strong my friends and till next Microsoft update don’t let your code have a meltdown.

 
Leave a comment

Posted by on April 15, 2013 in ASP.Net, C#, HTML

 

Tags: , , , , , ,